1 /* 2 * Copyright 2015 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 17 package android.media; 18 19 import android.graphics.ImageFormat; 20 import android.graphics.PixelFormat; 21 import android.graphics.Rect; 22 import android.hardware.camera2.utils.SurfaceUtils; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.util.Size; 27 import android.view.Surface; 28 29 import dalvik.system.VMRuntime; 30 31 import java.lang.ref.WeakReference; 32 import java.nio.ByteBuffer; 33 import java.nio.ByteOrder; 34 import java.nio.NioUtils; 35 import java.util.List; 36 import java.util.concurrent.CopyOnWriteArrayList; 37 38 /** 39 * <p> 40 * The ImageWriter class allows an application to produce Image data into a 41 * {@link android.view.Surface}, and have it be consumed by another component 42 * like {@link android.hardware.camera2.CameraDevice CameraDevice}. 43 * </p> 44 * <p> 45 * Several Android API classes can provide input {@link android.view.Surface 46 * Surface} objects for ImageWriter to produce data into, including 47 * {@link MediaCodec MediaCodec} (encoder), 48 * {@link android.hardware.camera2.CameraCaptureSession CameraCaptureSession} 49 * (reprocessing input), {@link ImageReader}, etc. 50 * </p> 51 * <p> 52 * The input Image data is encapsulated in {@link Image} objects. To produce 53 * Image data into a destination {@link android.view.Surface Surface}, the 54 * application can get an input Image via {@link #dequeueInputImage} then write 55 * Image data into it. Multiple such {@link Image} objects can be dequeued at 56 * the same time and queued back in any order, up to the number specified by the 57 * {@code maxImages} constructor parameter. 58 * </p> 59 * <p> 60 * If the application already has an Image from {@link ImageReader}, the 61 * application can directly queue this Image into ImageWriter (via 62 * {@link #queueInputImage}), potentially with zero buffer copies. For the 63 * {@link ImageFormat#PRIVATE PRIVATE} format Images produced by 64 * {@link ImageReader}, this is the only way to send Image data to ImageWriter, 65 * as the Image data aren't accessible by the application. 66 * </p> 67 * Once new input Images are queued into an ImageWriter, it's up to the 68 * downstream components (e.g. {@link ImageReader} or 69 * {@link android.hardware.camera2.CameraDevice}) to consume the Images. If the 70 * downstream components cannot consume the Images at least as fast as the 71 * ImageWriter production rate, the {@link #dequeueInputImage} call will 72 * eventually block and the application will have to drop input frames. 73 * </p> 74 * <p> 75 * If the consumer component that provided the input {@link android.view.Surface Surface} 76 * abandons the {@link android.view.Surface Surface}, {@link #queueInputImage queueing} 77 * or {@link #dequeueInputImage dequeueing} an {@link Image} will throw an 78 * {@link IllegalStateException}. 79 * </p> 80 */ 81 public class ImageWriter implements AutoCloseable { 82 private final Object mListenerLock = new Object(); 83 private OnImageReleasedListener mListener; 84 private ListenerHandler mListenerHandler; 85 private long mNativeContext; 86 87 // Field below is used by native code, do not access or modify. 88 private int mWriterFormat; 89 90 private final int mMaxImages; 91 // Keep track of the currently dequeued Image. This need to be thread safe as the images 92 // could be closed by different threads (e.g., application thread and GC thread). 93 private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>(); 94 private int mEstimatedNativeAllocBytes; 95 96 /** 97 * <p> 98 * Create a new ImageWriter. 99 * </p> 100 * <p> 101 * The {@code maxImages} parameter determines the maximum number of 102 * {@link Image} objects that can be be dequeued from the 103 * {@code ImageWriter} simultaneously. Requesting more buffers will use up 104 * more memory, so it is important to use only the minimum number necessary. 105 * </p> 106 * <p> 107 * The input Image size and format depend on the Surface that is provided by 108 * the downstream consumer end-point. 109 * </p> 110 * 111 * @param surface The destination Surface this writer produces Image data 112 * into. 113 * @param maxImages The maximum number of Images the user will want to 114 * access simultaneously for producing Image data. This should be 115 * as small as possible to limit memory use. Once maxImages 116 * Images are dequeued by the user, one of them has to be queued 117 * back before a new Image can be dequeued for access via 118 * {@link #dequeueInputImage()}. 119 * @return a new ImageWriter instance. 120 */ newInstance(Surface surface, int maxImages)121 public static ImageWriter newInstance(Surface surface, int maxImages) { 122 return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN); 123 } 124 125 /** 126 * <p> 127 * Create a new ImageWriter with given number of max Images and format. 128 * </p> 129 * <p> 130 * The {@code maxImages} parameter determines the maximum number of 131 * {@link Image} objects that can be be dequeued from the 132 * {@code ImageWriter} simultaneously. Requesting more buffers will use up 133 * more memory, so it is important to use only the minimum number necessary. 134 * </p> 135 * <p> 136 * The format specifies the image format of this ImageWriter. The format 137 * from the {@code surface} will be overridden with this format. For example, 138 * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default 139 * format may be {@link PixelFormat#RGBA_8888}. If the application creates an ImageWriter 140 * with this surface and {@link ImageFormat#PRIVATE}, this ImageWriter will be able to operate 141 * with {@link ImageFormat#PRIVATE} Images. 142 * </p> 143 * <p> 144 * Note that the consumer end-point may or may not be able to support Images with different 145 * format, for such case, the application should only use this method if the consumer is able 146 * to consume such images. 147 * </p> 148 * <p> 149 * The input Image size depends on the Surface that is provided by 150 * the downstream consumer end-point. 151 * </p> 152 * 153 * @param surface The destination Surface this writer produces Image data 154 * into. 155 * @param maxImages The maximum number of Images the user will want to 156 * access simultaneously for producing Image data. This should be 157 * as small as possible to limit memory use. Once maxImages 158 * Images are dequeued by the user, one of them has to be queued 159 * back before a new Image can be dequeued for access via 160 * {@link #dequeueInputImage()}. 161 * @param format The format of this ImageWriter. It can be any valid format specified by 162 * {@link ImageFormat} or {@link PixelFormat}. 163 * 164 * @return a new ImageWriter instance. 165 * @hide 166 */ newInstance(Surface surface, int maxImages, int format)167 public static ImageWriter newInstance(Surface surface, int maxImages, int format) { 168 if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { 169 throw new IllegalArgumentException("Invalid format is specified: " + format); 170 } 171 return new ImageWriter(surface, maxImages, format); 172 } 173 174 /** 175 * @hide 176 */ ImageWriter(Surface surface, int maxImages, int format)177 protected ImageWriter(Surface surface, int maxImages, int format) { 178 if (surface == null || maxImages < 1) { 179 throw new IllegalArgumentException("Illegal input argument: surface " + surface 180 + ", maxImages: " + maxImages); 181 } 182 183 mMaxImages = maxImages; 184 185 if (format == ImageFormat.UNKNOWN) { 186 format = SurfaceUtils.getSurfaceFormat(surface); 187 } 188 // Note that the underlying BufferQueue is working in synchronous mode 189 // to avoid dropping any buffers. 190 mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format); 191 192 // Estimate the native buffer allocation size and register it so it gets accounted for 193 // during GC. Note that this doesn't include the buffers required by the buffer queue 194 // itself and the buffers requested by the producer. 195 // Only include memory for 1 buffer, since actually accounting for the memory used is 196 // complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some 197 // size. 198 Size surfSize = SurfaceUtils.getSurfaceSize(surface); 199 mEstimatedNativeAllocBytes = 200 ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(), 201 format, /*buffer count*/ 1); 202 VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes); 203 } 204 205 /** 206 * <p> 207 * Maximum number of Images that can be dequeued from the ImageWriter 208 * simultaneously (for example, with {@link #dequeueInputImage()}). 209 * </p> 210 * <p> 211 * An Image is considered dequeued after it's returned by 212 * {@link #dequeueInputImage()} from ImageWriter, and until the Image is 213 * sent back to ImageWriter via {@link #queueInputImage}, or 214 * {@link Image#close()}. 215 * </p> 216 * <p> 217 * Attempting to dequeue more than {@code maxImages} concurrently will 218 * result in the {@link #dequeueInputImage()} function throwing an 219 * {@link IllegalStateException}. 220 * </p> 221 * 222 * @return Maximum number of Images that can be dequeued from this 223 * ImageWriter. 224 * @see #dequeueInputImage 225 * @see #queueInputImage 226 * @see Image#close 227 */ getMaxImages()228 public int getMaxImages() { 229 return mMaxImages; 230 } 231 232 /** 233 * <p> 234 * Dequeue the next available input Image for the application to produce 235 * data into. 236 * </p> 237 * <p> 238 * This method requests a new input Image from ImageWriter. The application 239 * owns this Image after this call. Once the application fills the Image 240 * data, it is expected to return this Image back to ImageWriter for 241 * downstream consumer components (e.g. 242 * {@link android.hardware.camera2.CameraDevice}) to consume. The Image can 243 * be returned to ImageWriter via {@link #queueInputImage} or 244 * {@link Image#close()}. 245 * </p> 246 * <p> 247 * This call will block if all available input images have been queued by 248 * the application and the downstream consumer has not yet consumed any. 249 * When an Image is consumed by the downstream consumer and released, an 250 * {@link OnImageReleasedListener#onImageReleased} callback will be fired, 251 * which indicates that there is one input Image available. For non- 252 * {@link ImageFormat#PRIVATE PRIVATE} formats ( 253 * {@link ImageWriter#getFormat()} != {@link ImageFormat#PRIVATE}), it is 254 * recommended to dequeue the next Image only after this callback is fired, 255 * in the steady state. 256 * </p> 257 * <p> 258 * If the format of ImageWriter is {@link ImageFormat#PRIVATE PRIVATE} ( 259 * {@link ImageWriter#getFormat()} == {@link ImageFormat#PRIVATE}), the 260 * image buffer is inaccessible to the application, and calling this method 261 * will result in an {@link IllegalStateException}. Instead, the application 262 * should acquire images from some other component (e.g. an 263 * {@link ImageReader}), and queue them directly to this ImageWriter via the 264 * {@link ImageWriter#queueInputImage queueInputImage()} method. 265 * </p> 266 * 267 * @return The next available input Image from this ImageWriter. 268 * @throws IllegalStateException if {@code maxImages} Images are currently 269 * dequeued, or the ImageWriter format is 270 * {@link ImageFormat#PRIVATE PRIVATE}, or the input 271 * {@link android.view.Surface Surface} has been abandoned by the 272 * consumer component that provided the {@link android.view.Surface Surface}. 273 * @see #queueInputImage 274 * @see Image#close 275 */ dequeueInputImage()276 public Image dequeueInputImage() { 277 if (mWriterFormat == ImageFormat.PRIVATE) { 278 throw new IllegalStateException( 279 "PRIVATE format ImageWriter doesn't support this operation since the images are" 280 + " inaccessible to the application!"); 281 } 282 283 if (mDequeuedImages.size() >= mMaxImages) { 284 throw new IllegalStateException("Already dequeued max number of Images " + mMaxImages); 285 } 286 WriterSurfaceImage newImage = new WriterSurfaceImage(this); 287 nativeDequeueInputImage(mNativeContext, newImage); 288 mDequeuedImages.add(newImage); 289 newImage.mIsImageValid = true; 290 return newImage; 291 } 292 293 /** 294 * <p> 295 * Queue an input {@link Image} back to ImageWriter for the downstream 296 * consumer to access. 297 * </p> 298 * <p> 299 * The input {@link Image} could be from ImageReader (acquired via 300 * {@link ImageReader#acquireNextImage} or 301 * {@link ImageReader#acquireLatestImage}), or from this ImageWriter 302 * (acquired via {@link #dequeueInputImage}). In the former case, the Image 303 * data will be moved to this ImageWriter. Note that the Image properties 304 * (size, format, strides, etc.) must be the same as the properties of the 305 * images dequeued from this ImageWriter, or this method will throw an 306 * {@link IllegalArgumentException}. In the latter case, the application has 307 * filled the input image with data. This method then passes the filled 308 * buffer to the downstream consumer. In both cases, it's up to the caller 309 * to ensure that the Image timestamp (in nanoseconds) is correctly set, as 310 * the downstream component may want to use it to indicate the Image data 311 * capture time. 312 * </p> 313 * <p> 314 * After this method is called and the downstream consumer consumes and 315 * releases the Image, an {@link OnImageReleasedListener#onImageReleased} 316 * callback will fire. The application can use this callback to avoid 317 * sending Images faster than the downstream consumer processing rate in 318 * steady state. 319 * </p> 320 * <p> 321 * Passing in an Image from some other component (e.g. an 322 * {@link ImageReader}) requires a free input Image from this ImageWriter as 323 * the destination. In this case, this call will block, as 324 * {@link #dequeueInputImage} does, if there are no free Images available. 325 * To avoid blocking, the application should ensure that there is at least 326 * one free Image available in this ImageWriter before calling this method. 327 * </p> 328 * <p> 329 * After this call, the input Image is no longer valid for further access, 330 * as if the Image is {@link Image#close closed}. Attempting to access the 331 * {@link ByteBuffer ByteBuffers} returned by an earlier 332 * {@link Image.Plane#getBuffer Plane#getBuffer} call will result in an 333 * {@link IllegalStateException}. 334 * </p> 335 * 336 * @param image The Image to be queued back to ImageWriter for future 337 * consumption. 338 * @throws IllegalStateException if the image was already queued previously, 339 * or the image was aborted previously, or the input 340 * {@link android.view.Surface Surface} has been abandoned by the 341 * consumer component that provided the 342 * {@link android.view.Surface Surface}. 343 * @see #dequeueInputImage() 344 */ queueInputImage(Image image)345 public void queueInputImage(Image image) { 346 if (image == null) { 347 throw new IllegalArgumentException("image shouldn't be null"); 348 } 349 boolean ownedByMe = isImageOwnedByMe(image); 350 if (ownedByMe && !(((WriterSurfaceImage) image).mIsImageValid)) { 351 throw new IllegalStateException("Image from ImageWriter is invalid"); 352 } 353 354 // For images from other components, need to detach first, then attach. 355 if (!ownedByMe) { 356 if (!(image.getOwner() instanceof ImageReader)) { 357 throw new IllegalArgumentException("Only images from ImageReader can be queued to" 358 + " ImageWriter, other image source is not supported yet!"); 359 } 360 361 ImageReader prevOwner = (ImageReader) image.getOwner(); 362 363 prevOwner.detachImage(image); 364 attachAndQueueInputImage(image); 365 // This clears the native reference held by the original owner. 366 // When this Image is detached later by this ImageWriter, the 367 // native memory won't be leaked. 368 image.close(); 369 return; 370 } 371 372 Rect crop = image.getCropRect(); 373 nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top, 374 crop.right, crop.bottom); 375 376 /** 377 * Only remove and cleanup the Images that are owned by this 378 * ImageWriter. Images detached from other owners are only temporarily 379 * owned by this ImageWriter and will be detached immediately after they 380 * are released by downstream consumers, so there is no need to keep 381 * track of them in mDequeuedImages. 382 */ 383 if (ownedByMe) { 384 mDequeuedImages.remove(image); 385 // Do not call close here, as close is essentially cancel image. 386 WriterSurfaceImage wi = (WriterSurfaceImage) image; 387 wi.clearSurfacePlanes(); 388 wi.mIsImageValid = false; 389 } 390 } 391 392 /** 393 * Get the ImageWriter format. 394 * <p> 395 * This format may be different than the Image format returned by 396 * {@link Image#getFormat()}. However, if the ImageWriter format is 397 * {@link ImageFormat#PRIVATE PRIVATE}, calling {@link #dequeueInputImage()} 398 * will result in an {@link IllegalStateException}. 399 * </p> 400 * 401 * @return The ImageWriter format. 402 */ getFormat()403 public int getFormat() { 404 return mWriterFormat; 405 } 406 407 /** 408 * ImageWriter callback interface, used to to asynchronously notify the 409 * application of various ImageWriter events. 410 */ 411 public interface OnImageReleasedListener { 412 /** 413 * <p> 414 * Callback that is called when an input Image is released back to 415 * ImageWriter after the data consumption. 416 * </p> 417 * <p> 418 * The client can use this callback to be notified that an input Image 419 * has been consumed and released by the downstream consumer. More 420 * specifically, this callback will be fired for below cases: 421 * <li>The application dequeues an input Image via the 422 * {@link ImageWriter#dequeueInputImage dequeueInputImage()} method, 423 * uses it, and then queues it back to this ImageWriter via the 424 * {@link ImageWriter#queueInputImage queueInputImage()} method. After 425 * the downstream consumer uses and releases this image to this 426 * ImageWriter, this callback will be fired. This image will be 427 * available to be dequeued after this callback.</li> 428 * <li>The application obtains an Image from some other component (e.g. 429 * an {@link ImageReader}), uses it, and then queues it to this 430 * ImageWriter via {@link ImageWriter#queueInputImage queueInputImage()}. 431 * After the downstream consumer uses and releases this image to this 432 * ImageWriter, this callback will be fired.</li> 433 * </p> 434 * 435 * @param writer the ImageWriter the callback is associated with. 436 * @see ImageWriter 437 * @see Image 438 */ onImageReleased(ImageWriter writer)439 void onImageReleased(ImageWriter writer); 440 } 441 442 /** 443 * Register a listener to be invoked when an input Image is returned to the 444 * ImageWriter. 445 * 446 * @param listener The listener that will be run. 447 * @param handler The handler on which the listener should be invoked, or 448 * null if the listener should be invoked on the calling thread's 449 * looper. 450 * @throws IllegalArgumentException If no handler specified and the calling 451 * thread has no looper. 452 */ setOnImageReleasedListener(OnImageReleasedListener listener, Handler handler)453 public void setOnImageReleasedListener(OnImageReleasedListener listener, Handler handler) { 454 synchronized (mListenerLock) { 455 if (listener != null) { 456 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); 457 if (looper == null) { 458 throw new IllegalArgumentException( 459 "handler is null but the current thread is not a looper"); 460 } 461 if (mListenerHandler == null || mListenerHandler.getLooper() != looper) { 462 mListenerHandler = new ListenerHandler(looper); 463 } 464 mListener = listener; 465 } else { 466 mListener = null; 467 mListenerHandler = null; 468 } 469 } 470 } 471 472 /** 473 * Free up all the resources associated with this ImageWriter. 474 * <p> 475 * After calling this method, this ImageWriter cannot be used. Calling any 476 * methods on this ImageWriter and Images previously provided by 477 * {@link #dequeueInputImage()} will result in an 478 * {@link IllegalStateException}, and attempting to write into 479 * {@link ByteBuffer ByteBuffers} returned by an earlier 480 * {@link Image.Plane#getBuffer Plane#getBuffer} call will have undefined 481 * behavior. 482 * </p> 483 */ 484 @Override close()485 public void close() { 486 setOnImageReleasedListener(null, null); 487 for (Image image : mDequeuedImages) { 488 image.close(); 489 } 490 mDequeuedImages.clear(); 491 nativeClose(mNativeContext); 492 mNativeContext = 0; 493 494 if (mEstimatedNativeAllocBytes > 0) { 495 VMRuntime.getRuntime().registerNativeFree(mEstimatedNativeAllocBytes); 496 mEstimatedNativeAllocBytes = 0; 497 } 498 } 499 500 @Override finalize()501 protected void finalize() throws Throwable { 502 try { 503 close(); 504 } finally { 505 super.finalize(); 506 } 507 } 508 509 /** 510 * <p> 511 * Attach and queue input Image to this ImageWriter. 512 * </p> 513 * <p> 514 * When the format of an Image is {@link ImageFormat#PRIVATE PRIVATE}, or 515 * the source Image is so large that copying its data is too expensive, this 516 * method can be used to migrate the source Image into ImageWriter without a 517 * data copy, and then queue it to this ImageWriter. The source Image must 518 * be detached from its previous owner already, or this call will throw an 519 * {@link IllegalStateException}. 520 * </p> 521 * <p> 522 * After this call, the ImageWriter takes ownership of this Image. This 523 * ownership will automatically be removed from this writer after the 524 * consumer releases this Image, that is, after 525 * {@link OnImageReleasedListener#onImageReleased}. The caller is responsible for 526 * closing this Image through {@link Image#close()} to free up the resources 527 * held by this Image. 528 * </p> 529 * 530 * @param image The source Image to be attached and queued into this 531 * ImageWriter for downstream consumer to use. 532 * @throws IllegalStateException if the Image is not detached from its 533 * previous owner, or the Image is already attached to this 534 * ImageWriter, or the source Image is invalid. 535 */ attachAndQueueInputImage(Image image)536 private void attachAndQueueInputImage(Image image) { 537 if (image == null) { 538 throw new IllegalArgumentException("image shouldn't be null"); 539 } 540 if (isImageOwnedByMe(image)) { 541 throw new IllegalArgumentException( 542 "Can not attach an image that is owned ImageWriter already"); 543 } 544 /** 545 * Throw ISE if the image is not attachable, which means that it is 546 * either owned by other entity now, or completely non-attachable (some 547 * stand-alone images are not backed by native gralloc buffer, thus not 548 * attachable). 549 */ 550 if (!image.isAttachable()) { 551 throw new IllegalStateException("Image was not detached from last owner, or image " 552 + " is not detachable"); 553 } 554 555 // TODO: what if attach failed, throw RTE or detach a slot then attach? 556 // need do some cleanup to make sure no orphaned 557 // buffer caused leak. 558 Rect crop = image.getCropRect(); 559 nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(), 560 image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom); 561 } 562 563 /** 564 * This custom handler runs asynchronously so callbacks don't get queued 565 * behind UI messages. 566 */ 567 private final class ListenerHandler extends Handler { ListenerHandler(Looper looper)568 public ListenerHandler(Looper looper) { 569 super(looper, null, true /* async */); 570 } 571 572 @Override handleMessage(Message msg)573 public void handleMessage(Message msg) { 574 OnImageReleasedListener listener; 575 synchronized (mListenerLock) { 576 listener = mListener; 577 } 578 if (listener != null) { 579 listener.onImageReleased(ImageWriter.this); 580 } 581 } 582 } 583 584 /** 585 * Called from Native code when an Event happens. This may be called from an 586 * arbitrary Binder thread, so access to the ImageWriter must be 587 * synchronized appropriately. 588 */ postEventFromNative(Object selfRef)589 private static void postEventFromNative(Object selfRef) { 590 @SuppressWarnings("unchecked") 591 WeakReference<ImageWriter> weakSelf = (WeakReference<ImageWriter>) selfRef; 592 final ImageWriter iw = weakSelf.get(); 593 if (iw == null) { 594 return; 595 } 596 597 final Handler handler; 598 synchronized (iw.mListenerLock) { 599 handler = iw.mListenerHandler; 600 } 601 if (handler != null) { 602 handler.sendEmptyMessage(0); 603 } 604 } 605 606 /** 607 * <p> 608 * Abort the Images that were dequeued from this ImageWriter, and return 609 * them to this writer for reuse. 610 * </p> 611 * <p> 612 * This method is used for the cases where the application dequeued the 613 * Image, may have filled the data, but does not want the downstream 614 * component to consume it. The Image will be returned to this ImageWriter 615 * for reuse after this call, and the ImageWriter will immediately have an 616 * Image available to be dequeued. This aborted Image will be invisible to 617 * the downstream consumer, as if nothing happened. 618 * </p> 619 * 620 * @param image The Image to be aborted. 621 * @see #dequeueInputImage() 622 * @see Image#close() 623 */ abortImage(Image image)624 private void abortImage(Image image) { 625 if (image == null) { 626 throw new IllegalArgumentException("image shouldn't be null"); 627 } 628 629 if (!mDequeuedImages.contains(image)) { 630 throw new IllegalStateException("It is illegal to abort some image that is not" 631 + " dequeued yet"); 632 } 633 634 WriterSurfaceImage wi = (WriterSurfaceImage) image; 635 if (!wi.mIsImageValid) { 636 return; 637 } 638 639 /** 640 * We only need abort Images that are owned and dequeued by ImageWriter. 641 * For attached Images, no need to abort, as there are only two cases: 642 * attached + queued successfully, and attach failed. Neither of the 643 * cases need abort. 644 */ 645 cancelImage(mNativeContext, image); 646 mDequeuedImages.remove(image); 647 wi.clearSurfacePlanes(); 648 wi.mIsImageValid = false; 649 } 650 isImageOwnedByMe(Image image)651 private boolean isImageOwnedByMe(Image image) { 652 if (!(image instanceof WriterSurfaceImage)) { 653 return false; 654 } 655 WriterSurfaceImage wi = (WriterSurfaceImage) image; 656 if (wi.getOwner() != this) { 657 return false; 658 } 659 660 return true; 661 } 662 663 private static class WriterSurfaceImage extends android.media.Image { 664 private ImageWriter mOwner; 665 // This field is used by native code, do not access or modify. 666 private long mNativeBuffer; 667 private int mNativeFenceFd = -1; 668 private SurfacePlane[] mPlanes; 669 private int mHeight = -1; 670 private int mWidth = -1; 671 private int mFormat = -1; 672 // When this default timestamp is used, timestamp for the input Image 673 // will be generated automatically when queueInputBuffer is called. 674 private final long DEFAULT_TIMESTAMP = Long.MIN_VALUE; 675 private long mTimestamp = DEFAULT_TIMESTAMP; 676 WriterSurfaceImage(ImageWriter writer)677 public WriterSurfaceImage(ImageWriter writer) { 678 mOwner = writer; 679 } 680 681 @Override getFormat()682 public int getFormat() { 683 throwISEIfImageIsInvalid(); 684 685 if (mFormat == -1) { 686 mFormat = nativeGetFormat(); 687 } 688 return mFormat; 689 } 690 691 @Override getWidth()692 public int getWidth() { 693 throwISEIfImageIsInvalid(); 694 695 if (mWidth == -1) { 696 mWidth = nativeGetWidth(); 697 } 698 699 return mWidth; 700 } 701 702 @Override getHeight()703 public int getHeight() { 704 throwISEIfImageIsInvalid(); 705 706 if (mHeight == -1) { 707 mHeight = nativeGetHeight(); 708 } 709 710 return mHeight; 711 } 712 713 @Override getTimestamp()714 public long getTimestamp() { 715 throwISEIfImageIsInvalid(); 716 717 return mTimestamp; 718 } 719 720 @Override setTimestamp(long timestamp)721 public void setTimestamp(long timestamp) { 722 throwISEIfImageIsInvalid(); 723 724 mTimestamp = timestamp; 725 } 726 727 @Override getPlanes()728 public Plane[] getPlanes() { 729 throwISEIfImageIsInvalid(); 730 731 if (mPlanes == null) { 732 int numPlanes = ImageUtils.getNumPlanesForFormat(getFormat()); 733 mPlanes = nativeCreatePlanes(numPlanes, getOwner().getFormat()); 734 } 735 736 return mPlanes.clone(); 737 } 738 739 @Override isAttachable()740 boolean isAttachable() { 741 throwISEIfImageIsInvalid(); 742 // Don't allow Image to be detached from ImageWriter for now, as no 743 // detach API is exposed. 744 return false; 745 } 746 747 @Override getOwner()748 ImageWriter getOwner() { 749 throwISEIfImageIsInvalid(); 750 751 return mOwner; 752 } 753 754 @Override getNativeContext()755 long getNativeContext() { 756 throwISEIfImageIsInvalid(); 757 758 return mNativeBuffer; 759 } 760 761 @Override close()762 public void close() { 763 if (mIsImageValid) { 764 getOwner().abortImage(this); 765 } 766 } 767 768 @Override finalize()769 protected final void finalize() throws Throwable { 770 try { 771 close(); 772 } finally { 773 super.finalize(); 774 } 775 } 776 clearSurfacePlanes()777 private void clearSurfacePlanes() { 778 if (mIsImageValid && mPlanes != null) { 779 for (int i = 0; i < mPlanes.length; i++) { 780 if (mPlanes[i] != null) { 781 mPlanes[i].clearBuffer(); 782 mPlanes[i] = null; 783 } 784 } 785 } 786 } 787 788 private class SurfacePlane extends android.media.Image.Plane { 789 private ByteBuffer mBuffer; 790 final private int mPixelStride; 791 final private int mRowStride; 792 793 // SurfacePlane instance is created by native code when SurfaceImage#getPlanes() is 794 // called SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer)795 private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) { 796 mRowStride = rowStride; 797 mPixelStride = pixelStride; 798 mBuffer = buffer; 799 /** 800 * Set the byteBuffer order according to host endianness (native 801 * order), otherwise, the byteBuffer order defaults to 802 * ByteOrder.BIG_ENDIAN. 803 */ 804 mBuffer.order(ByteOrder.nativeOrder()); 805 } 806 807 @Override getRowStride()808 public int getRowStride() { 809 throwISEIfImageIsInvalid(); 810 return mRowStride; 811 } 812 813 @Override getPixelStride()814 public int getPixelStride() { 815 throwISEIfImageIsInvalid(); 816 return mPixelStride; 817 } 818 819 @Override getBuffer()820 public ByteBuffer getBuffer() { 821 throwISEIfImageIsInvalid(); 822 return mBuffer; 823 } 824 clearBuffer()825 private void clearBuffer() { 826 // Need null check first, as the getBuffer() may not be called 827 // before an Image is closed. 828 if (mBuffer == null) { 829 return; 830 } 831 832 if (mBuffer.isDirect()) { 833 NioUtils.freeDirectBuffer(mBuffer); 834 } 835 mBuffer = null; 836 } 837 838 } 839 840 // Create the SurfacePlane object and fill the information nativeCreatePlanes(int numPlanes, int writerFmt)841 private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes, int writerFmt); 842 nativeGetWidth()843 private synchronized native int nativeGetWidth(); 844 nativeGetHeight()845 private synchronized native int nativeGetHeight(); 846 nativeGetFormat()847 private synchronized native int nativeGetFormat(); 848 } 849 850 // Native implemented ImageWriter methods. nativeInit(Object weakSelf, Surface surface, int maxImgs, int format)851 private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs, 852 int format); 853 nativeClose(long nativeCtx)854 private synchronized native void nativeClose(long nativeCtx); 855 nativeDequeueInputImage(long nativeCtx, Image wi)856 private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi); 857 nativeQueueInputImage(long nativeCtx, Image image, long timestampNs, int left, int top, int right, int bottom)858 private synchronized native void nativeQueueInputImage(long nativeCtx, Image image, 859 long timestampNs, int left, int top, int right, int bottom); 860 nativeAttachAndQueueImage(long nativeCtx, long imageNativeBuffer, int imageFormat, long timestampNs, int left, int top, int right, int bottom)861 private synchronized native int nativeAttachAndQueueImage(long nativeCtx, 862 long imageNativeBuffer, int imageFormat, long timestampNs, int left, 863 int top, int right, int bottom); 864 cancelImage(long nativeCtx, Image image)865 private synchronized native void cancelImage(long nativeCtx, Image image); 866 867 /** 868 * We use a class initializer to allow the native code to cache some field 869 * offsets. 870 */ nativeClassInit()871 private static native void nativeClassInit(); 872 873 static { 874 System.loadLibrary("media_jni"); nativeClassInit()875 nativeClassInit(); 876 } 877 } 878