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