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