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