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