Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2013 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.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 
     30 /**
     31  * <p>The ImageReader class allows direct application access to image data
     32  * rendered into a {@link android.view.Surface}</p>
     33  *
     34  * <p>Several Android media API classes accept Surface objects as targets to
     35  * render to, including {@link MediaPlayer}, {@link MediaCodec}, and
     36  * {@link android.renderscript.Allocation RenderScript Allocations}. The image
     37  * sizes and formats that can be used with each source vary, and should be
     38  * checked in the documentation for the specific API.</p>
     39  *
     40  * <p>The image data is encapsulated in {@link Image} objects, and multiple such
     41  * objects can be accessed at the same time, up to the number specified by the
     42  * {@code maxImages} constructor parameter. New images sent to an ImageReader
     43  * through its {@link Surface} are queued until accessed through the {@link #acquireLatestImage}
     44  * or {@link #acquireNextImage} call. Due to memory limits, an image source will
     45  * eventually stall or drop Images in trying to render to the Surface if the
     46  * ImageReader does not obtain and release Images at a rate equal to the
     47  * production rate.</p>
     48  */
     49 public class ImageReader implements AutoCloseable {
     50 
     51     /**
     52      * Returned by nativeImageSetup when acquiring the image was successful.
     53      */
     54     private static final int ACQUIRE_SUCCESS = 0;
     55     /**
     56      * Returned by nativeImageSetup when we couldn't acquire the buffer,
     57      * because there were no buffers available to acquire.
     58      */
     59     private static final int ACQUIRE_NO_BUFS = 1;
     60     /**
     61      * Returned by nativeImageSetup when we couldn't acquire the buffer
     62      * because the consumer has already acquired {@maxImages} and cannot
     63      * acquire more than that.
     64      */
     65     private static final int ACQUIRE_MAX_IMAGES = 2;
     66 
     67     /**
     68      * <p>Create a new reader for images of the desired size and format.</p>
     69      *
     70      * <p>The {@code maxImages} parameter determines the maximum number of {@link Image}
     71      * objects that can be be acquired from the {@code ImageReader}
     72      * simultaneously. Requesting more buffers will use up more memory, so it is
     73      * important to use only the minimum number necessary for the use case.</p>
     74      *
     75      * <p>The valid sizes and formats depend on the source of the image
     76      * data.</p>
     77      *
     78      * @param width
     79      *            The width in pixels of the Images that this reader will produce.
     80      * @param height
     81      *            The height in pixels of the Images that this reader will produce.
     82      * @param format
     83      *            The format of the Image that this reader will produce. This
     84      *            must be one of the {@link android.graphics.ImageFormat} or
     85      *            {@link android.graphics.PixelFormat} constants. Note that
     86      *            not all formats is supported, like ImageFormat.NV21.
     87      * @param maxImages
     88      *            The maximum number of images the user will want to
     89      *            access simultaneously. This should be as small as possible to limit
     90      *            memory use. Once maxImages Images are obtained by the user, one of them
     91      *            has to be released before a new Image will become available for access
     92      *            through {@link #acquireLatestImage()} or {@link #acquireNextImage()}.
     93      *            Must be greater than 0.
     94      *
     95      * @see Image
     96      */
     97     public static ImageReader newInstance(int width, int height, int format, int maxImages) {
     98         return new ImageReader(width, height, format, maxImages);
     99     }
    100 
    101     /**
    102      * @hide
    103      */
    104     protected ImageReader(int width, int height, int format, int maxImages) {
    105         mWidth = width;
    106         mHeight = height;
    107         mFormat = format;
    108         mMaxImages = maxImages;
    109 
    110         if (width < 1 || height < 1) {
    111             throw new IllegalArgumentException(
    112                 "The image dimensions must be positive");
    113         }
    114         if (mMaxImages < 1) {
    115             throw new IllegalArgumentException(
    116                 "Maximum outstanding image count must be at least 1");
    117         }
    118 
    119         if (format == ImageFormat.NV21) {
    120             throw new IllegalArgumentException(
    121                     "NV21 format is not supported");
    122         }
    123 
    124         mNumPlanes = getNumPlanesFromFormat();
    125 
    126         nativeInit(new WeakReference<ImageReader>(this), width, height, format, maxImages);
    127 
    128         mSurface = nativeGetSurface();
    129     }
    130 
    131     /**
    132      * The width of each {@link Image}, in pixels.
    133      *
    134      * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with
    135      * {@link #acquireNextImage}) will have the same dimensions as specified in
    136      * {@link #newInstance}.</p>
    137      *
    138      * @return the width of an Image
    139      */
    140     public int getWidth() {
    141         return mWidth;
    142     }
    143 
    144     /**
    145      * The height of each {@link Image}, in pixels.
    146      *
    147      * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with
    148      * {@link #acquireNextImage}) will have the same dimensions as specified in
    149      * {@link #newInstance}.</p>
    150      *
    151      * @return the height of an Image
    152      */
    153     public int getHeight() {
    154         return mHeight;
    155     }
    156 
    157     /**
    158      * The {@link ImageFormat image format} of each Image.
    159      *
    160      * <p>ImageReader guarantees that all {@link Image Images} acquired from ImageReader
    161      *  (for example, with {@link #acquireNextImage}) will have the same format as specified in
    162      * {@link #newInstance}.</p>
    163      *
    164      * @return the format of an Image
    165      *
    166      * @see ImageFormat
    167      */
    168     public int getImageFormat() {
    169         return mFormat;
    170     }
    171 
    172     /**
    173      * Maximum number of images that can be acquired from the ImageReader by any time (for example,
    174      * with {@link #acquireNextImage}).
    175      *
    176      * <p>An image is considered acquired after it's returned by a function from ImageReader, and
    177      * until the Image is {@link Image#close closed} to release the image back to the ImageReader.
    178      * </p>
    179      *
    180      * <p>Attempting to acquire more than {@code maxImages} concurrently will result in the
    181      * acquire function throwing a {@link IllegalStateException}. Furthermore,
    182      * while the max number of images have been acquired by the ImageReader user, the producer
    183      * enqueueing additional images may stall until at least one image has been released. </p>
    184      *
    185      * @return Maximum number of images for this ImageReader.
    186      *
    187      * @see Image#close
    188      */
    189     public int getMaxImages() {
    190         return mMaxImages;
    191     }
    192 
    193     /**
    194      * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this
    195      * {@code ImageReader}.</p>
    196      *
    197      * <p>Until valid image data is rendered into this {@link Surface}, the
    198      * {@link #acquireNextImage} method will return {@code null}. Only one source
    199      * can be producing data into this Surface at the same time, although the
    200      * same {@link Surface} can be reused with a different API once the first source is
    201      * disconnected from the {@link Surface}.</p>
    202      *
    203      * @return A {@link Surface} to use for a drawing target for various APIs.
    204      */
    205     public Surface getSurface() {
    206         return mSurface;
    207     }
    208 
    209     /**
    210      * <p>
    211      * Acquire the latest {@link Image} from the ImageReader's queue, dropping older
    212      * {@link Image images}. Returns {@code null} if no new image is available.
    213      * </p>
    214      * <p>
    215      * This operation will acquire all the images possible from the ImageReader,
    216      * but {@link #close} all images that aren't the latest. This function is
    217      * recommended to use over {@link #acquireNextImage} for most use-cases, as it's
    218      * more suited for real-time processing.
    219      * </p>
    220      * <p>
    221      * Note that {@link #getMaxImages maxImages} should be at least 2 for
    222      * {@link #acquireLatestImage} to be any different than {@link #acquireNextImage} -
    223      * discarding all-but-the-newest {@link Image} requires temporarily acquiring two
    224      * {@link Image Images} at once. Or more generally, calling {@link #acquireLatestImage}
    225      * with less than two images of margin, that is
    226      * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected.
    227      * </p>
    228      * <p>
    229      * This operation will fail by throwing an {@link IllegalStateException} if
    230      * {@code maxImages} have been acquired with {@link #acquireLatestImage} or
    231      * {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage}
    232      * calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between
    233      * will exhaust the underlying queue. At such a time, {@link IllegalStateException}
    234      * will be thrown until more images are
    235      * released with {@link Image#close}.
    236      * </p>
    237      *
    238      * @return latest frame of image data, or {@code null} if no image data is available.
    239      * @throws IllegalStateException if too many images are currently acquired
    240      */
    241     public Image acquireLatestImage() {
    242         Image image = acquireNextImage();
    243         if (image == null) {
    244             return null;
    245         }
    246         try {
    247             for (;;) {
    248                 Image next = acquireNextImageNoThrowISE();
    249                 if (next == null) {
    250                     Image result = image;
    251                     image = null;
    252                     return result;
    253                 }
    254                 image.close();
    255                 image = next;
    256             }
    257         } finally {
    258             if (image != null) {
    259                 image.close();
    260             }
    261         }
    262     }
    263 
    264     /**
    265      * Don't throw IllegalStateException if there are too many images acquired.
    266      *
    267      * @return Image if acquiring succeeded, or null otherwise.
    268      *
    269      * @hide
    270      */
    271     public Image acquireNextImageNoThrowISE() {
    272         SurfaceImage si = new SurfaceImage();
    273         return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null;
    274     }
    275 
    276     /**
    277      * Attempts to acquire the next image from the underlying native implementation.
    278      *
    279      * <p>
    280      * Note that unexpected failures will throw at the JNI level.
    281      * </p>
    282      *
    283      * @param si A blank SurfaceImage.
    284      * @return One of the {@code ACQUIRE_*} codes that determine success or failure.
    285      *
    286      * @see #ACQUIRE_MAX_IMAGES
    287      * @see #ACQUIRE_NO_BUFS
    288      * @see #ACQUIRE_SUCCESS
    289      */
    290     private int acquireNextSurfaceImage(SurfaceImage si) {
    291 
    292         int status = nativeImageSetup(si);
    293 
    294         switch (status) {
    295             case ACQUIRE_SUCCESS:
    296                 si.createSurfacePlanes();
    297                 si.setImageValid(true);
    298             case ACQUIRE_NO_BUFS:
    299             case ACQUIRE_MAX_IMAGES:
    300                 break;
    301             default:
    302                 throw new AssertionError("Unknown nativeImageSetup return code " + status);
    303         }
    304 
    305         return status;
    306     }
    307 
    308     /**
    309      * <p>
    310      * Acquire the next Image from the ImageReader's queue. Returns {@code null} if
    311      * no new image is available.
    312      * </p>
    313      *
    314      * <p><i>Warning:</i> Consider using {@link #acquireLatestImage()} instead, as it will
    315      * automatically release older images, and allow slower-running processing routines to catch
    316      * up to the newest frame. Usage of {@link #acquireNextImage} is recommended for
    317      * batch/background processing. Incorrectly using this function can cause images to appear
    318      * with an ever-increasing delay, followed by a complete stall where no new images seem to
    319      * appear.
    320      * </p>
    321      *
    322      * <p>
    323      * This operation will fail by throwing an {@link IllegalStateException} if
    324      * {@code maxImages} have been acquired with {@link #acquireNextImage} or
    325      * {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or
    326      * {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without
    327      * calling {@link Image#close} in-between will exhaust the underlying queue. At such a time,
    328      * {@link IllegalStateException} will be thrown until more images are released with
    329      * {@link Image#close}.
    330      * </p>
    331      *
    332      * @return a new frame of image data, or {@code null} if no image data is available.
    333      * @throws IllegalStateException if {@code maxImages} images are currently acquired
    334      * @see #acquireLatestImage
    335      */
    336     public Image acquireNextImage() {
    337         SurfaceImage si = new SurfaceImage();
    338         int status = acquireNextSurfaceImage(si);
    339 
    340         switch (status) {
    341             case ACQUIRE_SUCCESS:
    342                 return si;
    343             case ACQUIRE_NO_BUFS:
    344                 return null;
    345             case ACQUIRE_MAX_IMAGES:
    346                 throw new IllegalStateException(
    347                         String.format(
    348                                 "maxImages (%d) has already been acquired, " +
    349                                 "call #close before acquiring more.", mMaxImages));
    350             default:
    351                 throw new AssertionError("Unknown nativeImageSetup return code " + status);
    352         }
    353     }
    354 
    355     /**
    356      * <p>Return the frame to the ImageReader for reuse.</p>
    357      */
    358     private void releaseImage(Image i) {
    359         if (! (i instanceof SurfaceImage) ) {
    360             throw new IllegalArgumentException(
    361                 "This image was not produced by an ImageReader");
    362         }
    363         SurfaceImage si = (SurfaceImage) i;
    364         if (si.getReader() != this) {
    365             throw new IllegalArgumentException(
    366                 "This image was not produced by this ImageReader");
    367         }
    368 
    369         si.clearSurfacePlanes();
    370         nativeReleaseImage(i);
    371         si.setImageValid(false);
    372     }
    373 
    374     /**
    375      * Register a listener to be invoked when a new image becomes available
    376      * from the ImageReader.
    377      *
    378      * @param listener
    379      *            The listener that will be run.
    380      * @param handler
    381      *            The handler on which the listener should be invoked, or null
    382      *            if the listener should be invoked on the calling thread's looper.
    383      * @throws IllegalArgumentException
    384      *            If no handler specified and the calling thread has no looper.
    385      */
    386     public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
    387         synchronized (mListenerLock) {
    388             if (listener != null) {
    389                 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
    390                 if (looper == null) {
    391                     throw new IllegalArgumentException(
    392                             "handler is null but the current thread is not a looper");
    393                 }
    394                 if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
    395                     mListenerHandler = new ListenerHandler(looper);
    396                 }
    397                 mListener = listener;
    398             } else {
    399                 mListener = null;
    400                 mListenerHandler = null;
    401             }
    402         }
    403     }
    404 
    405     /**
    406      * Callback interface for being notified that a new image is available.
    407      *
    408      * <p>
    409      * The onImageAvailable is called per image basis, that is, callback fires for every new frame
    410      * available from ImageReader.
    411      * </p>
    412      */
    413     public interface OnImageAvailableListener {
    414         /**
    415          * Callback that is called when a new image is available from ImageReader.
    416          *
    417          * @param reader the ImageReader the callback is associated with.
    418          * @see ImageReader
    419          * @see Image
    420          */
    421         void onImageAvailable(ImageReader reader);
    422     }
    423 
    424     /**
    425      * Free up all the resources associated with this ImageReader.
    426      *
    427      * <p>
    428      * After calling this method, this ImageReader can not be used. Calling
    429      * any methods on this ImageReader and Images previously provided by
    430      * {@link #acquireNextImage} or {@link #acquireLatestImage}
    431      * will result in an {@link IllegalStateException}, and attempting to read from
    432      * {@link ByteBuffer ByteBuffers} returned by an earlier
    433      * {@link Image.Plane#getBuffer Plane#getBuffer} call will
    434      * have undefined behavior.
    435      * </p>
    436      */
    437     @Override
    438     public void close() {
    439         setOnImageAvailableListener(null, null);
    440         nativeClose();
    441     }
    442 
    443     @Override
    444     protected void finalize() throws Throwable {
    445         try {
    446             close();
    447         } finally {
    448             super.finalize();
    449         }
    450     }
    451 
    452     /**
    453      * Only a subset of the formats defined in
    454      * {@link android.graphics.ImageFormat ImageFormat} and
    455      * {@link android.graphics.PixelFormat PixelFormat} are supported by
    456      * ImageReader. When reading RGB data from a surface, the formats defined in
    457      * {@link android.graphics.PixelFormat PixelFormat} can be used, when
    458      * reading YUV, JPEG or raw sensor data (for example, from camera or video
    459      * decoder), formats from {@link android.graphics.ImageFormat ImageFormat}
    460      * are used.
    461      */
    462     private int getNumPlanesFromFormat() {
    463         switch (mFormat) {
    464             case ImageFormat.YV12:
    465             case ImageFormat.YUV_420_888:
    466             case ImageFormat.NV21:
    467                 return 3;
    468             case ImageFormat.NV16:
    469                 return 2;
    470             case PixelFormat.RGB_565:
    471             case PixelFormat.RGBA_8888:
    472             case PixelFormat.RGBX_8888:
    473             case PixelFormat.RGB_888:
    474             case ImageFormat.JPEG:
    475             case ImageFormat.YUY2:
    476             case ImageFormat.Y8:
    477             case ImageFormat.Y16:
    478             case ImageFormat.RAW_SENSOR:
    479                 return 1;
    480             default:
    481                 throw new UnsupportedOperationException(
    482                         String.format("Invalid format specified %d", mFormat));
    483         }
    484     }
    485 
    486     /**
    487      * Called from Native code when an Event happens.
    488      *
    489      * This may be called from an arbitrary Binder thread, so access to the ImageReader must be
    490      * synchronized appropriately.
    491      */
    492     private static void postEventFromNative(Object selfRef) {
    493         @SuppressWarnings("unchecked")
    494         WeakReference<ImageReader> weakSelf = (WeakReference<ImageReader>)selfRef;
    495         final ImageReader ir = weakSelf.get();
    496         if (ir == null) {
    497             return;
    498         }
    499 
    500         final Handler handler;
    501         synchronized (ir.mListenerLock) {
    502             handler = ir.mListenerHandler;
    503         }
    504         if (handler != null) {
    505             handler.sendEmptyMessage(0);
    506         }
    507     }
    508 
    509 
    510     private final int mWidth;
    511     private final int mHeight;
    512     private final int mFormat;
    513     private final int mMaxImages;
    514     private final int mNumPlanes;
    515     private final Surface mSurface;
    516 
    517     private final Object mListenerLock = new Object();
    518     private OnImageAvailableListener mListener;
    519     private ListenerHandler mListenerHandler;
    520 
    521     /**
    522      * This field is used by native code, do not access or modify.
    523      */
    524     private long mNativeContext;
    525 
    526     /**
    527      * This custom handler runs asynchronously so callbacks don't get queued behind UI messages.
    528      */
    529     private final class ListenerHandler extends Handler {
    530         public ListenerHandler(Looper looper) {
    531             super(looper, null, true /*async*/);
    532         }
    533 
    534         @Override
    535         public void handleMessage(Message msg) {
    536             OnImageAvailableListener listener;
    537             synchronized (mListenerLock) {
    538                 listener = mListener;
    539             }
    540             if (listener != null) {
    541                 listener.onImageAvailable(ImageReader.this);
    542             }
    543         }
    544     }
    545 
    546     private class SurfaceImage extends android.media.Image {
    547         public SurfaceImage() {
    548             mIsImageValid = false;
    549         }
    550 
    551         @Override
    552         public void close() {
    553             if (mIsImageValid) {
    554                 ImageReader.this.releaseImage(this);
    555             }
    556         }
    557 
    558         public ImageReader getReader() {
    559             return ImageReader.this;
    560         }
    561 
    562         @Override
    563         public int getFormat() {
    564             if (mIsImageValid) {
    565                 return ImageReader.this.mFormat;
    566             } else {
    567                 throw new IllegalStateException("Image is already released");
    568             }
    569         }
    570 
    571         @Override
    572         public int getWidth() {
    573             if (mIsImageValid) {
    574                 return ImageReader.this.mWidth;
    575             } else {
    576                 throw new IllegalStateException("Image is already released");
    577             }
    578         }
    579 
    580         @Override
    581         public int getHeight() {
    582             if (mIsImageValid) {
    583                 return ImageReader.this.mHeight;
    584             } else {
    585                 throw new IllegalStateException("Image is already released");
    586             }
    587         }
    588 
    589         @Override
    590         public long getTimestamp() {
    591             if (mIsImageValid) {
    592                 return mTimestamp;
    593             } else {
    594                 throw new IllegalStateException("Image is already released");
    595             }
    596         }
    597 
    598         @Override
    599         public Plane[] getPlanes() {
    600             if (mIsImageValid) {
    601                 // Shallow copy is fine.
    602                 return mPlanes.clone();
    603             } else {
    604                 throw new IllegalStateException("Image is already released");
    605             }
    606         }
    607 
    608         @Override
    609         protected final void finalize() throws Throwable {
    610             try {
    611                 close();
    612             } finally {
    613                 super.finalize();
    614             }
    615         }
    616 
    617         private void setImageValid(boolean isValid) {
    618             mIsImageValid = isValid;
    619         }
    620 
    621         private boolean isImageValid() {
    622             return mIsImageValid;
    623         }
    624 
    625         private void clearSurfacePlanes() {
    626             if (mIsImageValid) {
    627                 for (int i = 0; i < mPlanes.length; i++) {
    628                     if (mPlanes[i] != null) {
    629                         mPlanes[i].clearBuffer();
    630                         mPlanes[i] = null;
    631                     }
    632                 }
    633             }
    634         }
    635 
    636         private void createSurfacePlanes() {
    637             mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes];
    638             for (int i = 0; i < ImageReader.this.mNumPlanes; i++) {
    639                 mPlanes[i] = nativeCreatePlane(i);
    640             }
    641         }
    642         private class SurfacePlane extends android.media.Image.Plane {
    643             // SurfacePlane instance is created by native code when a new SurfaceImage is created
    644             private SurfacePlane(int index, int rowStride, int pixelStride) {
    645                 mIndex = index;
    646                 mRowStride = rowStride;
    647                 mPixelStride = pixelStride;
    648             }
    649 
    650             @Override
    651             public ByteBuffer getBuffer() {
    652                 if (SurfaceImage.this.isImageValid() == false) {
    653                     throw new IllegalStateException("Image is already released");
    654                 }
    655                 if (mBuffer != null) {
    656                     return mBuffer;
    657                 } else {
    658                     mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex);
    659                     // Set the byteBuffer order according to host endianness (native order),
    660                     // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN.
    661                     return mBuffer.order(ByteOrder.nativeOrder());
    662                 }
    663             }
    664 
    665             @Override
    666             public int getPixelStride() {
    667                 if (SurfaceImage.this.isImageValid()) {
    668                     return mPixelStride;
    669                 } else {
    670                     throw new IllegalStateException("Image is already released");
    671                 }
    672             }
    673 
    674             @Override
    675             public int getRowStride() {
    676                 if (SurfaceImage.this.isImageValid()) {
    677                     return mRowStride;
    678                 } else {
    679                     throw new IllegalStateException("Image is already released");
    680                 }
    681             }
    682 
    683             private void clearBuffer() {
    684                 mBuffer = null;
    685             }
    686 
    687             final private int mIndex;
    688             final private int mPixelStride;
    689             final private int mRowStride;
    690 
    691             private ByteBuffer mBuffer;
    692         }
    693 
    694         /**
    695          * This field is used to keep track of native object and used by native code only.
    696          * Don't modify.
    697          */
    698         private long mLockedBuffer;
    699 
    700         /**
    701          * This field is set by native code during nativeImageSetup().
    702          */
    703         private long mTimestamp;
    704 
    705         private SurfacePlane[] mPlanes;
    706         private boolean mIsImageValid;
    707 
    708         private synchronized native ByteBuffer nativeImageGetBuffer(int idx);
    709         private synchronized native SurfacePlane nativeCreatePlane(int idx);
    710     }
    711 
    712     private synchronized native void nativeInit(Object weakSelf, int w, int h,
    713                                                     int fmt, int maxImgs);
    714     private synchronized native void nativeClose();
    715     private synchronized native void nativeReleaseImage(Image i);
    716     private synchronized native Surface nativeGetSurface();
    717 
    718     /**
    719      * @return A return code {@code ACQUIRE_*}
    720      *
    721      * @see #ACQUIRE_SUCCESS
    722      * @see #ACQUIRE_NO_BUFS
    723      * @see #ACQUIRE_MAX_IMAGES
    724      */
    725     private synchronized native int nativeImageSetup(Image i);
    726 
    727     /**
    728      * We use a class initializer to allow the native code to cache some
    729      * field offsets.
    730      */
    731     private static native void nativeClassInit();
    732     static {
    733         System.loadLibrary("media_jni");
    734         nativeClassInit();
    735     }
    736 }
    737