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 <= 0 or the height <= 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 <= 0 or height is <= 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