• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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