• 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.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