Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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.graphics;
     18 
     19 import java.lang.ref.WeakReference;
     20 
     21 import android.annotation.Nullable;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.Message;
     25 import android.view.Surface;
     26 
     27 /**
     28  * Captures frames from an image stream as an OpenGL ES texture.
     29  *
     30  * <p>The image stream may come from either camera preview or video decode. A
     31  * {@link android.view.Surface} created from a SurfaceTexture can be used as an output
     32  * destination for the {@link android.hardware.camera2}, {@link android.media.MediaCodec},
     33  * {@link android.media.MediaPlayer}, and {@link android.renderscript.Allocation} APIs.
     34  * When {@link #updateTexImage} is called, the contents of the texture object specified
     35  * when the SurfaceTexture was created are updated to contain the most recent image from the image
     36  * stream.  This may cause some frames of the stream to be skipped.
     37  *
     38  * <p>A SurfaceTexture may also be used in place of a SurfaceHolder when specifying the output
     39  * destination of the older {@link android.hardware.Camera} API. Doing so will cause all the
     40  * frames from the image stream to be sent to the SurfaceTexture object rather than to the device's
     41  * display.
     42  *
     43  * <p>When sampling from the texture one should first transform the texture coordinates using the
     44  * matrix queried via {@link #getTransformMatrix(float[])}.  The transform matrix may change each
     45  * time {@link #updateTexImage} is called, so it should be re-queried each time the texture image
     46  * is updated.
     47  * This matrix transforms traditional 2D OpenGL ES texture coordinate column vectors of the form (s,
     48  * t, 0, 1) where s and t are on the inclusive interval [0, 1] to the proper sampling location in
     49  * the streamed texture.  This transform compensates for any properties of the image stream source
     50  * that cause it to appear different from a traditional OpenGL ES texture.  For example, sampling
     51  * from the bottom left corner of the image can be accomplished by transforming the column vector
     52  * (0, 0, 0, 1) using the queried matrix, while sampling from the top right corner of the image can
     53  * be done by transforming (1, 1, 0, 1).
     54  *
     55  * <p>The texture object uses the GL_TEXTURE_EXTERNAL_OES texture target, which is defined by the
     56  * <a href="http://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external.txt">
     57  * GL_OES_EGL_image_external</a> OpenGL ES extension.  This limits how the texture may be used.
     58  * Each time the texture is bound it must be bound to the GL_TEXTURE_EXTERNAL_OES target rather than
     59  * the GL_TEXTURE_2D target.  Additionally, any OpenGL ES 2.0 shader that samples from the texture
     60  * must declare its use of this extension using, for example, an "#extension
     61  * GL_OES_EGL_image_external : require" directive.  Such shaders must also access the texture using
     62  * the samplerExternalOES GLSL sampler type.
     63  *
     64  * <p>SurfaceTexture objects may be created on any thread.  {@link #updateTexImage} may only be
     65  * called on the thread with the OpenGL ES context that contains the texture object.  The
     66  * frame-available callback is called on an arbitrary thread, so unless special care is taken {@link
     67  * #updateTexImage} should not be called directly from the callback.
     68  */
     69 public class SurfaceTexture {
     70     private final Looper mCreatorLooper;
     71     private Handler mOnFrameAvailableHandler;
     72 
     73     /**
     74      * These fields are used by native code, do not access or modify.
     75      */
     76     private long mSurfaceTexture;
     77     private long mProducer;
     78     private long mFrameAvailableListener;
     79 
     80     /**
     81      * Callback interface for being notified that a new stream frame is available.
     82      */
     83     public interface OnFrameAvailableListener {
     84         void onFrameAvailable(SurfaceTexture surfaceTexture);
     85     }
     86 
     87     /**
     88      * Exception thrown when a SurfaceTexture couldn't be created or resized.
     89      *
     90      * @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException}
     91      * is used instead.
     92      */
     93     @SuppressWarnings("serial")
     94     @Deprecated
     95     public static class OutOfResourcesException extends Exception {
     96         public OutOfResourcesException() {
     97         }
     98         public OutOfResourcesException(String name) {
     99             super(name);
    100         }
    101     }
    102 
    103     /**
    104      * Construct a new SurfaceTexture to stream images to a given OpenGL texture.
    105      *
    106      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
    107      *
    108      * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
    109      */
    110     public SurfaceTexture(int texName) {
    111         this(texName, false);
    112     }
    113 
    114     /**
    115      * Construct a new SurfaceTexture to stream images to a given OpenGL texture.
    116      *
    117      * In single buffered mode the application is responsible for serializing access to the image
    118      * content buffer. Each time the image content is to be updated, the
    119      * {@link #releaseTexImage()} method must be called before the image content producer takes
    120      * ownership of the buffer. For example, when producing image content with the NDK
    121      * ANativeWindow_lock and ANativeWindow_unlockAndPost functions, {@link #releaseTexImage()}
    122      * must be called before each ANativeWindow_lock, or that call will fail. When producing
    123      * image content with OpenGL ES, {@link #releaseTexImage()} must be called before the first
    124      * OpenGL ES function call each frame.
    125      *
    126      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
    127      * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
    128      *
    129      * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
    130      */
    131     public SurfaceTexture(int texName, boolean singleBufferMode) {
    132         mCreatorLooper = Looper.myLooper();
    133         nativeInit(false, texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
    134     }
    135 
    136     /**
    137      * Construct a new SurfaceTexture to stream images to a given OpenGL texture.
    138      *
    139      * In single buffered mode the application is responsible for serializing access to the image
    140      * content buffer. Each time the image content is to be updated, the
    141      * {@link #releaseTexImage()} method must be called before the image content producer takes
    142      * ownership of the buffer. For example, when producing image content with the NDK
    143      * ANativeWindow_lock and ANativeWindow_unlockAndPost functions, {@link #releaseTexImage()}
    144      * must be called before each ANativeWindow_lock, or that call will fail. When producing
    145      * image content with OpenGL ES, {@link #releaseTexImage()} must be called before the first
    146      * OpenGL ES function call each frame.
    147      *
    148      * Unlike {@link #SurfaceTexture(int, boolean)}, which takes an OpenGL texture object name,
    149      * this constructor creates the SurfaceTexture in detached mode. A texture name must be passed
    150      * in using {@link #attachToGLContext} before calling {@link #releaseTexImage()} and producing
    151      * image content using OpenGL ES.
    152      *
    153      * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
    154      *
    155      * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
    156      * @hide
    157      */
    158     public SurfaceTexture(boolean singleBufferMode) {
    159         mCreatorLooper = Looper.myLooper();
    160         nativeInit(true, 0, singleBufferMode, new WeakReference<SurfaceTexture>(this));
    161     }
    162 
    163     /**
    164      * Register a callback to be invoked when a new image frame becomes available to the
    165      * SurfaceTexture.
    166      * <p>
    167      * The callback may be called on an arbitrary thread, so it is not
    168      * safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the
    169      * thread invoking the callback.
    170      * </p>
    171      *
    172      * @param listener The listener to use, or null to remove the listener.
    173      */
    174     public void setOnFrameAvailableListener(@Nullable OnFrameAvailableListener listener) {
    175         setOnFrameAvailableListener(listener, null);
    176     }
    177 
    178     /**
    179      * Register a callback to be invoked when a new image frame becomes available to the
    180      * SurfaceTexture.
    181      * <p>
    182      * If a handler is specified, the callback will be invoked on that handler's thread.
    183      * If no handler is specified, then the callback may be called on an arbitrary thread,
    184      * so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES
    185      * context to the thread invoking the callback.
    186      * </p>
    187      *
    188      * @param listener The listener to use, or null to remove the listener.
    189      * @param handler The handler on which the listener should be invoked, or null
    190      * to use an arbitrary thread.
    191      */
    192     public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener,
    193             @Nullable Handler handler) {
    194         if (listener != null) {
    195             // Although we claim the thread is arbitrary, earlier implementation would
    196             // prefer to send the callback on the creating looper or the main looper
    197             // so we preserve this behavior here.
    198             Looper looper = handler != null ? handler.getLooper() :
    199                     mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
    200             mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) {
    201                 @Override
    202                 public void handleMessage(Message msg) {
    203                     listener.onFrameAvailable(SurfaceTexture.this);
    204                 }
    205             };
    206         } else {
    207             mOnFrameAvailableHandler = null;
    208         }
    209     }
    210 
    211     /**
    212      * Set the default size of the image buffers.  The image producer may override the buffer size,
    213      * in which case the producer-set buffer size will be used, not the default size set by this
    214      * method.  Both video and camera based image producers do override the size.  This method may
    215      * be used to set the image size when producing images with {@link android.graphics.Canvas} (via
    216      * {@link android.view.Surface#lockCanvas}), or OpenGL ES (via an EGLSurface).
    217      *
    218      * The new default buffer size will take effect the next time the image producer requests a
    219      * buffer to fill.  For {@link android.graphics.Canvas} this will be the next time {@link
    220      * android.view.Surface#lockCanvas} is called.  For OpenGL ES, the EGLSurface should be
    221      * destroyed (via eglDestroySurface), made not-current (via eglMakeCurrent), and then recreated
    222      * (via eglCreateWindowSurface) to ensure that the new default size has taken effect.
    223      *
    224      * The width and height parameters must be no greater than the minimum of
    225      * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see
    226      * {@link javax.microedition.khronos.opengles.GL10#glGetIntegerv glGetIntegerv}).
    227      * An error due to invalid dimensions might not be reported until
    228      * updateTexImage() is called.
    229      */
    230     public void setDefaultBufferSize(int width, int height) {
    231         nativeSetDefaultBufferSize(width, height);
    232     }
    233 
    234     /**
    235      * Update the texture image to the most recent frame from the image stream.  This may only be
    236      * called while the OpenGL ES context that owns the texture is current on the calling thread.
    237      * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
    238      */
    239     public void updateTexImage() {
    240         nativeUpdateTexImage();
    241     }
    242 
    243     /**
    244      * Releases the the texture content. This is needed in single buffered mode to allow the image
    245      * content producer to take ownership of the image buffer.
    246      * For more information see {@link #SurfaceTexture(int, boolean)}.
    247      */
    248     public void releaseTexImage() {
    249         nativeReleaseTexImage();
    250     }
    251 
    252     /**
    253      * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
    254      * This call must be made with the OpenGL ES context current on the calling thread.  The OpenGL
    255      * ES texture object will be deleted as a result of this call.  After calling this method all
    256      * calls to {@link #updateTexImage} will throw an {@link java.lang.IllegalStateException} until
    257      * a successful call to {@link #attachToGLContext} is made.
    258      *
    259      * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
    260      * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
    261      * context at a time.
    262      */
    263     public void detachFromGLContext() {
    264         int err = nativeDetachFromGLContext();
    265         if (err != 0) {
    266             throw new RuntimeException("Error during detachFromGLContext (see logcat for details)");
    267         }
    268     }
    269 
    270     /**
    271      * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread.  A
    272      * new OpenGL ES texture object is created and populated with the SurfaceTexture image frame
    273      * that was current at the time of the last call to {@link #detachFromGLContext}.  This new
    274      * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
    275      *
    276      * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
    277      * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
    278      * context at a time.
    279      *
    280      * @param texName The name of the OpenGL ES texture that will be created.  This texture name
    281      * must be unusued in the OpenGL ES context that is current on the calling thread.
    282      */
    283     public void attachToGLContext(int texName) {
    284         int err = nativeAttachToGLContext(texName);
    285         if (err != 0) {
    286             throw new RuntimeException("Error during attachToGLContext (see logcat for details)");
    287         }
    288     }
    289 
    290     /**
    291      * Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set by
    292      * the most recent call to updateTexImage.
    293      *
    294      * This transform matrix maps 2D homogeneous texture coordinates of the form (s, t, 0, 1) with s
    295      * and t in the inclusive range [0, 1] to the texture coordinate that should be used to sample
    296      * that location from the texture.  Sampling the texture outside of the range of this transform
    297      * is undefined.
    298      *
    299      * The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via
    300      * the glLoadMatrixf or glUniformMatrix4fv functions.
    301      *
    302      * @param mtx the array into which the 4x4 matrix will be stored.  The array must have exactly
    303      *     16 elements.
    304      */
    305     public void getTransformMatrix(float[] mtx) {
    306         // Note we intentionally don't check mtx for null, so this will result in a
    307         // NullPointerException. But it's safe because it happens before the call to native.
    308         if (mtx.length != 16) {
    309             throw new IllegalArgumentException();
    310         }
    311         nativeGetTransformMatrix(mtx);
    312     }
    313 
    314     /**
    315      * Retrieve the timestamp associated with the texture image set by the most recent call to
    316      * updateTexImage.
    317      *
    318      * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
    319      * should be unaffected by time-of-day adjustments, and for a camera should be strictly
    320      * monotonic but for a MediaPlayer may be reset when the position is set.  The
    321      * specific meaning and zero point of the timestamp depends on the source providing images to
    322      * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
    323      * generally be compared across SurfaceTexture instances, or across multiple program
    324      * invocations. It is mostly useful for determining time offsets between subsequent frames.
    325      */
    326 
    327     public long getTimestamp() {
    328         return nativeGetTimestamp();
    329     }
    330 
    331     /**
    332      * release() frees all the buffers and puts the SurfaceTexture into the
    333      * 'abandoned' state. Once put in this state the SurfaceTexture can never
    334      * leave it. When in the 'abandoned' state, all methods of the
    335      * IGraphicBufferProducer interface will fail with the NO_INIT error.
    336      *
    337      * Note that while calling this method causes all the buffers to be freed
    338      * from the perspective of the the SurfaceTexture, if there are additional
    339      * references on the buffers (e.g. if a buffer is referenced by a client or
    340      * by OpenGL ES as a texture) then those buffer will remain allocated.
    341      *
    342      * Always call this method when you are done with SurfaceTexture. Failing
    343      * to do so may delay resource deallocation for a significant amount of
    344      * time.
    345      */
    346     public void release() {
    347         nativeRelease();
    348     }
    349 
    350     /**
    351      * Returns true if the SurfaceTexture was released
    352      * @hide
    353      */
    354     public boolean isReleased() {
    355         return nativeIsReleased();
    356     }
    357 
    358     @Override
    359     protected void finalize() throws Throwable {
    360         try {
    361             nativeFinalize();
    362         } finally {
    363             super.finalize();
    364         }
    365     }
    366 
    367     /**
    368      * This method is invoked from native code only.
    369      */
    370     @SuppressWarnings({"UnusedDeclaration"})
    371     private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {
    372         SurfaceTexture st = weakSelf.get();
    373         if (st != null) {
    374             Handler handler = st.mOnFrameAvailableHandler;
    375             if (handler != null) {
    376                 handler.sendEmptyMessage(0);
    377             }
    378         }
    379     }
    380 
    381     private native void nativeInit(boolean isDetached, int texName,
    382             boolean singleBufferMode, WeakReference<SurfaceTexture> weakSelf)
    383             throws Surface.OutOfResourcesException;
    384     private native void nativeFinalize();
    385     private native void nativeGetTransformMatrix(float[] mtx);
    386     private native long nativeGetTimestamp();
    387     private native void nativeSetDefaultBufferSize(int width, int height);
    388     private native void nativeUpdateTexImage();
    389     private native void nativeReleaseTexImage();
    390     private native int nativeDetachFromGLContext();
    391     private native int nativeAttachToGLContext(int texName);
    392     private native int nativeGetQueuedCount();
    393     private native void nativeRelease();
    394     private native boolean nativeIsReleased();
    395 
    396     /*
    397      * We use a class initializer to allow the native code to cache some
    398      * field offsets.
    399      */
    400     private static native void nativeClassInit();
    401     static { nativeClassInit(); }
    402 }
    403