TextureView.java revision 451ce44a18e4c48f8a43aa250957f76967a35d31
1/*
2 * Copyright (C) 2011 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.view;
18
19import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.Paint;
22import android.graphics.SurfaceTexture;
23import android.util.AttributeSet;
24import android.util.Log;
25
26/**
27 * <p>A TextureView can be used to display a content stream. Such a content
28 * stream can for instance be a video or an OpenGL scene. The content stream
29 * can come from the application's process as well as a remote process.</p>
30 *
31 * <p>TextureView can only be used in a hardware accelerated window. When
32 * rendered in software, TextureView will draw nothing.</p>
33 *
34 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
35 * window but behaves as a regular View. This key difference allows a
36 * TextureView to be moved, transformed, animated, etc. For instance, you
37 * can make a TextureView semi-translucent by calling
38 * <code>myView.setAlpha(0.5f)</code>.</p>
39 *
40 * <p>Using a TextureView is simple: all you need to do is get its
41 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
42 * render content. The following example demonstrates how to render the
43 * camera preview into a TextureView:</p>
44 *
45 * <pre>
46 *  public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
47 *      private Camera mCamera;
48 *      private TextureView mTextureView;
49 *
50 *      protected void onCreate(Bundle savedInstanceState) {
51 *          super.onCreate(savedInstanceState);
52 *
53 *          mTextureView = new TextureView(this);
54 *          mTextureView.setSurfaceTextureListener(this);
55 *
56 *          setContentView(mTextureView);
57 *      }
58 *
59 *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
60 *          mCamera = Camera.open();
61 *
62 *          try {
63 *              mCamera.setPreviewTexture(surface);
64 *              mCamera.startPreview();
65 *          } catch (IOException ioe) {
66 *              // Something bad happened
67 *          }
68 *      }
69 *
70 *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
71 *          // Ignored, Camera does all the work for us
72 *      }
73 *
74 *      public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
75 *          mCamera.stopPreview();
76 *          mCamera.release();
77 *      }
78 *  }
79 * </pre>
80 *
81 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
82 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
83 * It is important to know that a SurfaceTexture is available only after the
84 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
85 * been invoked.) It is therefore highly recommended you use a listener to
86 * be notified when the SurfaceTexture becomes available.</p>
87 *
88 * @see SurfaceView
89 * @see SurfaceTexture
90 */
91public class TextureView extends View {
92    private HardwareLayer mLayer;
93    private SurfaceTexture mSurface;
94    private SurfaceTextureListener mListener;
95
96    private final Runnable mUpdateLayerAction = new Runnable() {
97        @Override
98        public void run() {
99            updateLayer();
100        }
101    };
102    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
103
104    /**
105     * Creates a new TextureView.
106     *
107     * @param context The context to associate this view with.
108     */
109    public TextureView(Context context) {
110        super(context);
111        init();
112    }
113
114    /**
115     * Creates a new TextureView.
116     *
117     * @param context The context to associate this view with.
118     * @param attrs The attributes of the XML tag that is inflating the view.
119     */
120    @SuppressWarnings({"UnusedDeclaration"})
121    public TextureView(Context context, AttributeSet attrs) {
122        super(context, attrs);
123        init();
124    }
125
126    /**
127     * Creates a new TextureView.
128     *
129     * @param context The context to associate this view with.
130     * @param attrs The attributes of the XML tag that is inflating the view.
131     * @param defStyle The default style to apply to this view. If 0, no style
132     *        will be applied (beyond what is included in the theme). This may
133     *        either be an attribute resource, whose value will be retrieved
134     *        from the current theme, or an explicit style resource.
135     */
136    @SuppressWarnings({"UnusedDeclaration"})
137    public TextureView(Context context, AttributeSet attrs, int defStyle) {
138        super(context, attrs, defStyle);
139        init();
140    }
141
142    private void init() {
143        mLayerPaint = new Paint();
144    }
145
146    @Override
147    protected void onAttachedToWindow() {
148        super.onAttachedToWindow();
149
150        if (!isHardwareAccelerated()) {
151            Log.w("TextureView", "A TextureView or a subclass can only be "
152                    + "used with hardware acceleration enabled.");
153        }
154    }
155
156    @Override
157    protected void onDetachedFromWindow() {
158        super.onDetachedFromWindow();
159
160        if (isHardwareAccelerated() && mLayer != null) {
161            if (mListener != null) {
162                mListener.onSurfaceTextureDestroyed(mSurface);
163            }
164
165            mLayer.destroy();
166            mSurface = null;
167            mLayer = null;
168        }
169    }
170
171    /**
172     * The layer type of a TextureView is ignored since a TextureView is always
173     * considered to act as a hardware layer. The optional paint supplied to this
174     * method will however be taken into account when rendering the content of
175     * this TextureView.
176     *
177     * @param layerType The ype of layer to use with this view, must be one of
178     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
179     *        {@link #LAYER_TYPE_HARDWARE}
180     * @param paint The paint used to compose the layer. This argument is optional
181     *        and can be null. It is ignored when the layer type is
182     *        {@link #LAYER_TYPE_NONE}
183     */
184    @Override
185    public void setLayerType(int layerType, Paint paint) {
186        if (paint != mLayerPaint) {
187            mLayerPaint = paint;
188            invalidate();
189        }
190    }
191
192    /**
193     * Always returns {@link #LAYER_TYPE_HARDWARE}.
194     */
195    @Override
196    public int getLayerType() {
197        return LAYER_TYPE_HARDWARE;
198    }
199
200    /**
201     * Calling this method has no effect.
202     */
203    @Override
204    public void buildLayer() {
205    }
206
207    /**
208     * Subclasses of TextureView cannot do their own rendering
209     * with the {@link Canvas} object.
210     *
211     * @param canvas The Canvas to which the View is rendered.
212     */
213    @Override
214    public final void draw(Canvas canvas) {
215        super.draw(canvas);
216    }
217
218    /**
219     * Subclasses of TextureView cannot do their own rendering
220     * with the {@link Canvas} object.
221     *
222     * @param canvas The Canvas to which the View is rendered.
223     */
224    @Override
225    protected final void onDraw(Canvas canvas) {
226    }
227
228    @Override
229    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
230        super.onSizeChanged(w, h, oldw, oldh);
231        if (mSurface != null) {
232            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
233            if (mListener != null) {
234                mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
235            }
236        }
237    }
238
239    @Override
240    HardwareLayer getHardwareLayer() {
241        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
242            return null;
243        }
244
245        if (mLayer == null) {
246            mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer();
247            mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer);
248            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
249
250            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
251                @Override
252                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
253                    // Per SurfaceTexture's documentation, the callback may be invoked
254                    // from an arbitrary thread
255                    post(mUpdateLayerAction);
256                }
257            };
258            mSurface.setOnFrameAvailableListener(mUpdateListener);
259
260            if (mListener != null) {
261                mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
262            }
263        }
264
265        return mLayer;
266    }
267
268    @Override
269    protected void onVisibilityChanged(View changedView, int visibility) {
270        super.onVisibilityChanged(changedView, visibility);
271
272        if (mSurface != null) {
273            // When the view becomes invisible, stop updating it, it's a waste of CPU
274            // To cancel updates, the easiest thing to do is simply to remove the
275            // updates listener
276            if (visibility == VISIBLE) {
277                mSurface.setOnFrameAvailableListener(mUpdateListener);
278                updateLayer();
279            } else {
280                mSurface.setOnFrameAvailableListener(null);
281            }
282        }
283    }
284
285    private void updateLayer() {
286        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
287            return;
288        }
289
290        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mSurface);
291
292        invalidate();
293    }
294
295    /**
296     * Returns the {@link SurfaceTexture} used by this view. This method
297     * may return null if the view is not attached to a window.
298     */
299    public SurfaceTexture getSurfaceTexture() {
300        return mSurface;
301    }
302
303    /**
304     * Returns the {@link SurfaceTextureListener} currently associated with this
305     * texture view.
306     *
307     * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
308     * @see SurfaceTextureListener
309     */
310    public SurfaceTextureListener getSurfaceTextureListener() {
311        return mListener;
312    }
313
314    /**
315     * Sets the {@link SurfaceTextureListener} used to listen to surface
316     * texture events.
317     *
318     * @see #getSurfaceTextureListener()
319     * @see SurfaceTextureListener
320     */
321    public void setSurfaceTextureListener(SurfaceTextureListener listener) {
322        mListener = listener;
323    }
324
325    /**
326     * This listener can be used to be notified when the surface texture
327     * associated with this texture view is available.
328     */
329    public static interface SurfaceTextureListener {
330        /**
331         * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
332         *
333         * @param surface The surface returned by
334         *                {@link android.view.TextureView#getSurfaceTexture()}
335         * @param width The width of the surface
336         * @param height The height of the surface
337         */
338        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
339
340        /**
341         * Invoked when the {@link SurfaceTexture}'s buffers size changed.
342         *
343         * @param surface The surface returned by
344         *                {@link android.view.TextureView#getSurfaceTexture()}
345         * @param width The new width of the surface
346         * @param height The new height of the surface
347         */
348        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
349
350        /**
351         * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
352         * After this method is invoked, no rendering should happen inside the surface
353         * texture.
354         *
355         * @param surface The surface about to be destroyed
356         */
357        public void onSurfaceTextureDestroyed(SurfaceTexture surface);
358    }
359
360    private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height);
361}
362