TextureView.java revision 58f4edb7701bf20925468fa5fd1a06a461ff085b
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.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Paint;
23import android.graphics.SurfaceTexture;
24import android.util.AttributeSet;
25import android.util.Log;
26
27/**
28 * <p>A TextureView can be used to display a content stream. Such a content
29 * stream can for instance be a video or an OpenGL scene. The content stream
30 * can come from the application's process as well as a remote process.</p>
31 *
32 * <p>TextureView can only be used in a hardware accelerated window. When
33 * rendered in software, TextureView will draw nothing.</p>
34 *
35 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
36 * window but behaves as a regular View. This key difference allows a
37 * TextureView to be moved, transformed, animated, etc. For instance, you
38 * can make a TextureView semi-translucent by calling
39 * <code>myView.setAlpha(0.5f)</code>.</p>
40 *
41 * <p>Using a TextureView is simple: all you need to do is get its
42 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
43 * render content. The following example demonstrates how to render the
44 * camera preview into a TextureView:</p>
45 *
46 * <pre>
47 *  public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
48 *      private Camera mCamera;
49 *      private TextureView mTextureView;
50 *
51 *      protected void onCreate(Bundle savedInstanceState) {
52 *          super.onCreate(savedInstanceState);
53 *
54 *          mTextureView = new TextureView(this);
55 *          mTextureView.setSurfaceTextureListener(this);
56 *
57 *          setContentView(mTextureView);
58 *      }
59 *
60 *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
61 *          mCamera = Camera.open();
62 *
63 *          try {
64 *              mCamera.setPreviewTexture(surface);
65 *              mCamera.startPreview();
66 *          } catch (IOException ioe) {
67 *              // Something bad happened
68 *          }
69 *      }
70 *
71 *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
72 *          // Ignored, Camera does all the work for us
73 *      }
74 *
75 *      public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
76 *          mCamera.stopPreview();
77 *          mCamera.release();
78 *      }
79 *
80 *      public void onSurfaceTextureUpdated(SurfaceTexture surface) {
81 *          // Invoked every time there's a new Camera preview frame
82 *      }
83 *  }
84 * </pre>
85 *
86 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
87 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
88 * It is important to know that a SurfaceTexture is available only after the
89 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
90 * been invoked.) It is therefore highly recommended you use a listener to
91 * be notified when the SurfaceTexture becomes available.</p>
92 *
93 * @see SurfaceView
94 * @see SurfaceTexture
95 */
96public class TextureView extends View {
97    private static final String LOG_TAG = "TextureView";
98
99    private HardwareLayer mLayer;
100    private SurfaceTexture mSurface;
101    private SurfaceTextureListener mListener;
102
103    private boolean mOpaque = true;
104
105    private final Object[] mLock = new Object[0];
106    private boolean mUpdateLayer;
107
108    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
109
110    /**
111     * Creates a new TextureView.
112     *
113     * @param context The context to associate this view with.
114     */
115    public TextureView(Context context) {
116        super(context);
117        init();
118    }
119
120    /**
121     * Creates a new TextureView.
122     *
123     * @param context The context to associate this view with.
124     * @param attrs The attributes of the XML tag that is inflating the view.
125     */
126    @SuppressWarnings({"UnusedDeclaration"})
127    public TextureView(Context context, AttributeSet attrs) {
128        super(context, attrs);
129        init();
130    }
131
132    /**
133     * Creates a new TextureView.
134     *
135     * @param context The context to associate this view with.
136     * @param attrs The attributes of the XML tag that is inflating the view.
137     * @param defStyle The default style to apply to this view. If 0, no style
138     *        will be applied (beyond what is included in the theme). This may
139     *        either be an attribute resource, whose value will be retrieved
140     *        from the current theme, or an explicit style resource.
141     */
142    @SuppressWarnings({"UnusedDeclaration"})
143    public TextureView(Context context, AttributeSet attrs, int defStyle) {
144        super(context, attrs, defStyle);
145        init();
146    }
147
148    private void init() {
149        mLayerPaint = new Paint();
150    }
151
152    /**
153     * {@inheritDoc}
154     */
155    @Override
156    public boolean isOpaque() {
157        return mOpaque;
158    }
159
160    /**
161     * Indicates whether the content of this TextureView is opaque. The
162     * content is assumed to be opaque by default.
163     *
164     * @param opaque True if the content of this TextureView is opaque,
165     *               false otherwise
166     */
167    public void setOpaque(boolean opaque) {
168        if (opaque != mOpaque) {
169            mOpaque = opaque;
170            updateLayer();
171        }
172    }
173
174    @Override
175    protected void onAttachedToWindow() {
176        super.onAttachedToWindow();
177
178        if (!isHardwareAccelerated()) {
179            Log.w(LOG_TAG, "A TextureView or a subclass can only be "
180                    + "used with hardware acceleration enabled.");
181        }
182    }
183
184    @Override
185    protected void onDetachedFromWindow() {
186        super.onDetachedFromWindow();
187
188        if (isHardwareAccelerated() && mLayer != null) {
189            if (mListener != null) {
190                mListener.onSurfaceTextureDestroyed(mSurface);
191            }
192
193            mLayer.destroy();
194            mSurface = null;
195            mLayer = null;
196        }
197    }
198
199    /**
200     * The layer type of a TextureView is ignored since a TextureView is always
201     * considered to act as a hardware layer. The optional paint supplied to this
202     * method will however be taken into account when rendering the content of
203     * this TextureView.
204     *
205     * @param layerType The ype of layer to use with this view, must be one of
206     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
207     *        {@link #LAYER_TYPE_HARDWARE}
208     * @param paint The paint used to compose the layer. This argument is optional
209     *        and can be null. It is ignored when the layer type is
210     *        {@link #LAYER_TYPE_NONE}
211     */
212    @Override
213    public void setLayerType(int layerType, Paint paint) {
214        if (paint != mLayerPaint) {
215            mLayerPaint = paint;
216            invalidate();
217        }
218    }
219
220    /**
221     * Always returns {@link #LAYER_TYPE_HARDWARE}.
222     */
223    @Override
224    public int getLayerType() {
225        return LAYER_TYPE_HARDWARE;
226    }
227
228    /**
229     * Calling this method has no effect.
230     */
231    @Override
232    public void buildLayer() {
233    }
234
235    /**
236     * Subclasses of TextureView cannot do their own rendering
237     * with the {@link Canvas} object.
238     *
239     * @param canvas The Canvas to which the View is rendered.
240     */
241    @Override
242    public final void draw(Canvas canvas) {
243        applyUpdate();
244    }
245
246    /**
247     * Subclasses of TextureView cannot do their own rendering
248     * with the {@link Canvas} object.
249     *
250     * @param canvas The Canvas to which the View is rendered.
251     */
252    @Override
253    protected final void onDraw(Canvas canvas) {
254    }
255
256    @Override
257    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
258        super.onSizeChanged(w, h, oldw, oldh);
259        if (mSurface != null) {
260            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
261            if (mListener != null) {
262                mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight());
263            }
264        }
265    }
266
267    @Override
268    HardwareLayer getHardwareLayer() {
269        if (mLayer == null) {
270            if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
271                return null;
272            }
273
274            mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
275            mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
276            nSetDefaultBufferSize(mSurface, getWidth(), getHeight());
277
278            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
279                @Override
280                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
281                    // Per SurfaceTexture's documentation, the callback may be invoked
282                    // from an arbitrary thread
283                    synchronized (mLock) {
284                        mUpdateLayer = true;
285                    }
286                    postInvalidateDelayed(0);
287                }
288            };
289            mSurface.setOnFrameAvailableListener(mUpdateListener);
290
291            if (mListener != null) {
292                mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
293            }
294        }
295
296        applyUpdate();
297
298        return mLayer;
299    }
300
301    @Override
302    protected void onVisibilityChanged(View changedView, int visibility) {
303        super.onVisibilityChanged(changedView, visibility);
304
305        if (mSurface != null) {
306            // When the view becomes invisible, stop updating it, it's a waste of CPU
307            // To cancel updates, the easiest thing to do is simply to remove the
308            // updates listener
309            if (visibility == VISIBLE) {
310                mSurface.setOnFrameAvailableListener(mUpdateListener);
311                updateLayer();
312            } else {
313                mSurface.setOnFrameAvailableListener(null);
314            }
315        }
316    }
317
318    private void updateLayer() {
319        mUpdateLayer = true;
320        invalidate();
321    }
322
323    private void applyUpdate() {
324        if (mLayer == null) {
325            return;
326        }
327
328        synchronized (mLock) {
329            if (mUpdateLayer) {
330                mUpdateLayer = false;
331            } else {
332                return;
333            }
334        }
335
336        mLayer.update(getWidth(), getHeight(), mOpaque);
337
338        if (mListener != null) {
339            mListener.onSurfaceTextureUpdated(mSurface);
340        }
341    }
342
343    /**
344     * <p>Returns a {@link android.graphics.Bitmap} representation of the content
345     * of the associated surface texture. If the surface texture is not available,
346     * this method returns null.</p>
347     *
348     * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
349     * pixel format and its dimensions are the same as this view's.</p>
350     *
351     * <p><strong>Do not</strong> invoke this method from a drawing method
352     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
353     *
354     * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
355     *
356     * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
357     *         texture is not available or the width &lt;= 0 or the height &lt;= 0
358     *
359     * @see #isAvailable()
360     * @see #getBitmap(android.graphics.Bitmap)
361     * @see #getBitmap(int, int)
362     */
363    public Bitmap getBitmap() {
364        return getBitmap(getWidth(), getHeight());
365    }
366
367    /**
368     * <p>Returns a {@link android.graphics.Bitmap} representation of the content
369     * of the associated surface texture. If the surface texture is not available,
370     * this method returns null.</p>
371     *
372     * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888}
373     * pixel format.</p>
374     *
375     * <p><strong>Do not</strong> invoke this method from a drawing method
376     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
377     *
378     * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
379     *
380     * @param width The width of the bitmap to create
381     * @param height The height of the bitmap to create
382     *
383     * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
384     *         texture is not available or width is &lt;= 0 or height is &lt;= 0
385     *
386     * @see #isAvailable()
387     * @see #getBitmap(android.graphics.Bitmap)
388     * @see #getBitmap()
389     */
390    public Bitmap getBitmap(int width, int height) {
391        if (isAvailable() && width > 0 && height > 0) {
392            return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888));
393        }
394        return null;
395    }
396
397    /**
398     * <p>Copies the content of this view's surface texture into the specified
399     * bitmap. If the surface texture is not available, the copy is not executed.
400     * The content of the surface texture will be scaled to fit exactly inside
401     * the specified bitmap.</p>
402     *
403     * <p><strong>Do not</strong> invoke this method from a drawing method
404     * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
405     *
406     * <p>If an error occurs, the bitmap is left unchanged.</p>
407     *
408     * @param bitmap The bitmap to copy the content of the surface texture into,
409     *               cannot be null, all configurations are supported
410     *
411     * @return The bitmap specified as a parameter
412     *
413     * @see #isAvailable()
414     * @see #getBitmap(int, int)
415     * @see #getBitmap()
416     */
417    public Bitmap getBitmap(Bitmap bitmap) {
418        if (bitmap != null && isAvailable()) {
419            mLayer.copyInto(bitmap);
420        }
421        return bitmap;
422    }
423
424    /**
425     * Returns true if the {@link SurfaceTexture} associated with this
426     * TextureView is available for rendering. When this method returns
427     * true, {@link #getSurfaceTexture()} returns a valid surface texture.
428     */
429    public boolean isAvailable() {
430        return mSurface != null;
431    }
432
433    /**
434     * Returns the {@link SurfaceTexture} used by this view. This method
435     * may return null if the view is not attached to a window or if the surface
436     * texture has not been initialized yet.
437     *
438     * @see #isAvailable()
439     */
440    public SurfaceTexture getSurfaceTexture() {
441        return mSurface;
442    }
443
444    /**
445     * Returns the {@link SurfaceTextureListener} currently associated with this
446     * texture view.
447     *
448     * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
449     * @see SurfaceTextureListener
450     */
451    public SurfaceTextureListener getSurfaceTextureListener() {
452        return mListener;
453    }
454
455    /**
456     * Sets the {@link SurfaceTextureListener} used to listen to surface
457     * texture events.
458     *
459     * @see #getSurfaceTextureListener()
460     * @see SurfaceTextureListener
461     */
462    public void setSurfaceTextureListener(SurfaceTextureListener listener) {
463        mListener = listener;
464    }
465
466    /**
467     * This listener can be used to be notified when the surface texture
468     * associated with this texture view is available.
469     */
470    public static interface SurfaceTextureListener {
471        /**
472         * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
473         *
474         * @param surface The surface returned by
475         *                {@link android.view.TextureView#getSurfaceTexture()}
476         * @param width The width of the surface
477         * @param height The height of the surface
478         */
479        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
480
481        /**
482         * Invoked when the {@link SurfaceTexture}'s buffers size changed.
483         *
484         * @param surface The surface returned by
485         *                {@link android.view.TextureView#getSurfaceTexture()}
486         * @param width The new width of the surface
487         * @param height The new height of the surface
488         */
489        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
490
491        /**
492         * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
493         * After this method is invoked, no rendering should happen inside the surface
494         * texture.
495         *
496         * @param surface The surface about to be destroyed
497         */
498        public void onSurfaceTextureDestroyed(SurfaceTexture surface);
499
500        /**
501         * Invoked when the specified {@link SurfaceTexture} is updated through
502         * {@link SurfaceTexture#updateTexImage()}.
503         *
504         * @param surface The surface just updated
505         */
506        public void onSurfaceTextureUpdated(SurfaceTexture surface);
507    }
508
509    private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
510            int width, int height);
511}
512