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 {
onFrameAvailable(SurfaceTexture surfaceTexture)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 {
OutOfResourcesException()96         public OutOfResourcesException() {
97         }
OutOfResourcesException(String name)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      */
SurfaceTexture(int texName)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      */
SurfaceTexture(int texName, boolean singleBufferMode)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      */
SurfaceTexture(boolean singleBufferMode)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      */
setOnFrameAvailableListener(@ullable OnFrameAvailableListener listener)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      */
setOnFrameAvailableListener(@ullable final OnFrameAvailableListener listener, @Nullable Handler handler)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      */
setDefaultBufferSize(int width, int height)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      */
updateTexImage()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      */
releaseTexImage()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      */
detachFromGLContext()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      */
attachToGLContext(int texName)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      */
getTransformMatrix(float[] mtx)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 
getTimestamp()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      */
release()346     public void release() {
347         nativeRelease();
348     }
349 
350     /**
351      * Returns true if the SurfaceTexture was released
352      * @hide
353      */
isReleased()354     public boolean isReleased() {
355         return nativeIsReleased();
356     }
357 
358     @Override
finalize()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"})
postEventFromNative(WeakReference<SurfaceTexture> weakSelf)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 
nativeInit(boolean isDetached, int texName, boolean singleBufferMode, WeakReference<SurfaceTexture> weakSelf)381     private native void nativeInit(boolean isDetached, int texName,
382             boolean singleBufferMode, WeakReference<SurfaceTexture> weakSelf)
383             throws Surface.OutOfResourcesException;
nativeFinalize()384     private native void nativeFinalize();
nativeGetTransformMatrix(float[] mtx)385     private native void nativeGetTransformMatrix(float[] mtx);
nativeGetTimestamp()386     private native long nativeGetTimestamp();
nativeSetDefaultBufferSize(int width, int height)387     private native void nativeSetDefaultBufferSize(int width, int height);
nativeUpdateTexImage()388     private native void nativeUpdateTexImage();
nativeReleaseTexImage()389     private native void nativeReleaseTexImage();
nativeDetachFromGLContext()390     private native int nativeDetachFromGLContext();
nativeAttachToGLContext(int texName)391     private native int nativeAttachToGLContext(int texName);
nativeGetQueuedCount()392     private native int nativeGetQueuedCount();
nativeRelease()393     private native void nativeRelease();
nativeIsReleased()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      */
nativeClassInit()400     private static native void nativeClassInit();
nativeClassInit()401     static { nativeClassInit(); }
402 }
403