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 17 package android.view; 18 19 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.Context; 25 import android.graphics.Bitmap; 26 import android.graphics.Canvas; 27 import android.graphics.Matrix; 28 import android.graphics.Paint; 29 import android.graphics.RecordingCanvas; 30 import android.graphics.Rect; 31 import android.graphics.SurfaceTexture; 32 import android.graphics.TextureLayer; 33 import android.graphics.drawable.Drawable; 34 import android.os.Build; 35 import android.os.Trace; 36 import android.util.AttributeSet; 37 import android.util.Log; 38 import android.view.flags.Flags; 39 40 /** 41 * <p>A TextureView can be used to display a content stream, such as that 42 * coming from a camera preview, a video, or an OpenGL scene. The content stream 43 * can come from the application's process as well as a remote process.</p> 44 * 45 * <p>TextureView can only be used in a hardware accelerated window. When 46 * rendered in software, TextureView will draw nothing.</p> 47 * 48 * <p><b>TextureView vs. SurfaceView Capabilities</b></p> 49 50 * <p> 51 * <table> 52 * <tr> 53 * <th> </th> 54 * <th style="text-align: center;">TextureView</th> 55 * <th style="text-align: center;">SurfaceView</th> 56 * </tr> 57 * <tr> 58 * <td>Supports View alpha</td> 59 * <td style="text-align: center;">X</td> 60 * <td style="text-align: center;">U+</td> 61 * </tr> 62 * <tr> 63 * <td>Supports rotations</td> 64 * <td style="text-align: center;">X</td> 65 * <td style="text-align: center;"> </td> 66 * </tr> 67 * <tr> 68 * <td>Supports clipping</td> 69 * <td style="text-align: center;">X</td> 70 * <td style="text-align: center;"> </td> 71 * </tr> 72 * <tr> 73 * <td>HDR support</td> 74 * <td style="text-align: center;">Limited (on Android T+)</td> 75 * <td style="text-align: center;">Full</td> 76 * </tr> 77 * <tr> 78 * <td>Renders DRM content</td> 79 * <td style="text-align: center;"> </td> 80 * <td style="text-align: center;">X</td> 81 * </tr> 82 * </table> 83 * </p> 84 * 85 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate 86 * window but behaves as a regular View. This key difference allows a 87 * TextureView to have translucency, arbitrary rotations, and complex 88 * clipping. For example, you can make a TextureView semi-translucent by 89 * calling <code>myView.setAlpha(0.5f)</code>.</p> 90 * 91 * <p>One implication of this integration of TextureView into the view 92 * hierarchy is that it may have slower performance than 93 * SurfaceView. TextureView contents must be copied, internally, from the 94 * underlying surface into the view displaying those contents. For 95 * that reason, <b>SurfaceView is recommended as a more general solution 96 * to problems requiring rendering to surfaces.</b></p> 97 * 98 * <p>Using a TextureView is simple: all you need to do is get its 99 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to 100 * render content. The following example demonstrates how to render a video 101 * into a TextureView:</p> 102 * 103 * <pre> 104 * public class MyActivity extends Activity implements TextureView.SurfaceTextureListener { 105 * private MediaPlayer mMediaPlayer; 106 * private TextureView mTextureView; 107 * 108 * protected void onCreate(Bundle savedInstanceState) { 109 * super.onCreate(savedInstanceState); 110 * 111 * mMediaPlayer = new MediaPlayer(); 112 * 113 * mTextureView = new TextureView(this); 114 * mTextureView.setSurfaceTextureListener(this); 115 * setContentView(mTextureView); 116 * } 117 * 118 * public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, 119 * int width, int height) { 120 * AssetFileDescriptor fileDescriptor = // get file descriptor 121 * mMediaPlayer.setDataSource(fileDescriptor); 122 * mMediaPlayer.setSurface(new Surface(surfaceTexture)); 123 * mMediaPlayer.prepareAsync(); 124 * mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 125 * @Override 126 * public void onPrepared(MediaPlayer mp) { 127 * mMediaPlayer.start(); 128 * } 129 * }); 130 * } catch (IOException e) { 131 * e.printStackTrace(); 132 * } 133 * } 134 * 135 * @Override 136 * public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, 137 * int width, int height) { 138 * // Handle size change depending on media needs 139 * } 140 * 141 * @Override 142 * public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) { 143 * // Release unneeded resources 144 * mMediaPlayer.stop(); 145 * mMediaPlayer.release(); 146 * return true; 147 * } 148 * 149 * @Override 150 * public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) { 151 * // Invoked every time there's a new video frame 152 * } 153 * 154 * } 155 * </pre> 156 * 157 * <p>Similarly, TextureView can supply the surface needed for GL rendering or 158 * camera previews. Camera2 APIs require the surface created by TextureView, 159 * although developers are recommended to use the CameraX APIs instead, for which 160 * PreviewView creates its own TextureView or SurfaceView internally.</p> 161 * 162 * <p>A TextureView's SurfaceTexture can be obtained either by invoking 163 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}. 164 * It is important to know that a SurfaceTexture is available only after the 165 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has 166 * been invoked.) It is therefore highly recommended you use a listener to 167 * be notified when the SurfaceTexture becomes available.</p> 168 * 169 * <p>It is important to note that only one producer can use the TextureView. 170 * For instance, if you use a TextureView to display the camera preview, you 171 * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same 172 * time.</p> 173 * 174 * @see SurfaceView 175 * @see SurfaceTexture 176 */ 177 public class TextureView extends View { 178 private static final String LOG_TAG = "TextureView"; 179 180 @UnsupportedAppUsage 181 private TextureLayer mLayer; 182 @UnsupportedAppUsage 183 private SurfaceTexture mSurface; 184 private SurfaceTextureListener mListener; 185 private boolean mHadSurface; 186 187 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 188 private boolean mOpaque = true; 189 190 private final Matrix mMatrix = new Matrix(); 191 private boolean mMatrixChanged; 192 193 private final Object[] mLock = new Object[0]; 194 private boolean mUpdateLayer; 195 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 196 private boolean mUpdateSurface; 197 198 private Canvas mCanvas; 199 private int mSaveCount; 200 201 private final Object[] mNativeWindowLock = new Object[0]; 202 // Set by native code, do not write! 203 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 204 private long mNativeWindow; 205 // Used for VRR detecting "normal" frame rate rather than "high". This is the previous 206 // interval for drawing. This can be removed when NORMAL is the default rate for Views. 207 // (b/329156944) 208 private long mMinusTwoFrameIntervalMillis = 0; 209 // Used for VRR detecting "normal" frame rate rather than "high". This is the last 210 // frame time for drawing. This can be removed when NORMAL is the default rate for Views. 211 // (b/329156944) 212 private long mLastFrameTimeMillis = 0; 213 214 /** 215 * Creates a new TextureView. 216 * 217 * @param context The context to associate this view with. 218 */ TextureView(@onNull Context context)219 public TextureView(@NonNull Context context) { 220 super(context); 221 mRenderNode.setIsTextureView(); 222 } 223 224 /** 225 * Creates a new TextureView. 226 * 227 * @param context The context to associate this view with. 228 * @param attrs The attributes of the XML tag that is inflating the view. 229 */ TextureView(@onNull Context context, @Nullable AttributeSet attrs)230 public TextureView(@NonNull Context context, @Nullable AttributeSet attrs) { 231 super(context, attrs); 232 mRenderNode.setIsTextureView(); 233 } 234 235 /** 236 * Creates a new TextureView. 237 * 238 * @param context The context to associate this view with. 239 * @param attrs The attributes of the XML tag that is inflating the view. 240 * @param defStyleAttr An attribute in the current theme that contains a 241 * reference to a style resource that supplies default values for 242 * the view. Can be 0 to not look for defaults. 243 */ TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)244 public TextureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 245 super(context, attrs, defStyleAttr); 246 mRenderNode.setIsTextureView(); 247 } 248 249 /** 250 * Creates a new TextureView. 251 * 252 * @param context The context to associate this view with. 253 * @param attrs The attributes of the XML tag that is inflating the view. 254 * @param defStyleAttr An attribute in the current theme that contains a 255 * reference to a style resource that supplies default values for 256 * the view. Can be 0 to not look for defaults. 257 * @param defStyleRes A resource identifier of a style resource that 258 * supplies default values for the view, used only if 259 * defStyleAttr is 0 or can not be found in the theme. Can be 0 260 * to not look for defaults. 261 */ TextureView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)262 public TextureView(@NonNull Context context, @Nullable AttributeSet attrs, 263 int defStyleAttr, int defStyleRes) { 264 super(context, attrs, defStyleAttr, defStyleRes); 265 mRenderNode.setIsTextureView(); 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override isOpaque()272 public boolean isOpaque() { 273 return mOpaque; 274 } 275 276 /** 277 * Indicates whether the content of this TextureView is opaque. The 278 * content is assumed to be opaque by default. 279 * 280 * @param opaque True if the content of this TextureView is opaque, 281 * false otherwise 282 */ setOpaque(boolean opaque)283 public void setOpaque(boolean opaque) { 284 if (opaque != mOpaque) { 285 mOpaque = opaque; 286 if (mLayer != null) { 287 updateLayerAndInvalidate(); 288 } 289 } 290 } 291 292 @Override onAttachedToWindow()293 protected void onAttachedToWindow() { 294 super.onAttachedToWindow(); 295 296 if (!isHardwareAccelerated()) { 297 Log.w(LOG_TAG, "A TextureView or a subclass can only be " 298 + "used with hardware acceleration enabled."); 299 } 300 301 if (mHadSurface) { 302 invalidate(true); 303 mHadSurface = false; 304 } 305 } 306 307 /** @hide */ 308 @Override 309 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) onDetachedFromWindowInternal()310 protected void onDetachedFromWindowInternal() { 311 destroyHardwareLayer(); 312 releaseSurfaceTexture(); 313 super.onDetachedFromWindowInternal(); 314 } 315 316 /** 317 * @hide 318 */ 319 @Override 320 @UnsupportedAppUsage destroyHardwareResources()321 protected void destroyHardwareResources() { 322 super.destroyHardwareResources(); 323 destroyHardwareLayer(); 324 } 325 326 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) destroyHardwareLayer()327 private void destroyHardwareLayer() { 328 if (mLayer != null) { 329 mLayer.detachSurfaceTexture(); 330 mLayer.close(); 331 mLayer = null; 332 mMatrixChanged = true; 333 } 334 } 335 releaseSurfaceTexture()336 private void releaseSurfaceTexture() { 337 if (mSurface != null) { 338 boolean shouldRelease = true; 339 340 if (mListener != null) { 341 shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface); 342 } 343 344 synchronized (mNativeWindowLock) { 345 nDestroyNativeWindow(); 346 } 347 348 if (shouldRelease) { 349 mSurface.release(); 350 } 351 mSurface = null; 352 mHadSurface = true; 353 } 354 } 355 356 /** 357 * The layer type of a TextureView is ignored since a TextureView is always 358 * considered to act as a hardware layer. The optional paint supplied to this 359 * method will however be taken into account when rendering the content of 360 * this TextureView. 361 * 362 * @param layerType The type of layer to use with this view, must be one of 363 * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or 364 * {@link #LAYER_TYPE_HARDWARE} 365 * @param paint The paint used to compose the layer. This argument is optional 366 * and can be null. It is ignored when the layer type is 367 * {@link #LAYER_TYPE_NONE} 368 */ 369 @Override setLayerType(int layerType, @Nullable Paint paint)370 public void setLayerType(int layerType, @Nullable Paint paint) { 371 setLayerPaint(paint); 372 } 373 374 @Override setLayerPaint(@ullable Paint paint)375 public void setLayerPaint(@Nullable Paint paint) { 376 if (paint != mLayerPaint) { 377 mLayerPaint = paint; 378 invalidate(); 379 } 380 } 381 382 /** 383 * Always returns {@link #LAYER_TYPE_HARDWARE}. 384 */ 385 @Override getLayerType()386 public int getLayerType() { 387 return LAYER_TYPE_HARDWARE; 388 } 389 390 /** 391 * Calling this method has no effect. 392 */ 393 @Override buildLayer()394 public void buildLayer() { 395 } 396 397 @Override setForeground(Drawable foreground)398 public void setForeground(Drawable foreground) { 399 if (foreground != null && !sTextureViewIgnoresDrawableSetters) { 400 throw new UnsupportedOperationException( 401 "TextureView doesn't support displaying a foreground drawable"); 402 } 403 } 404 405 @Override setBackgroundDrawable(Drawable background)406 public void setBackgroundDrawable(Drawable background) { 407 if (background != null && !sTextureViewIgnoresDrawableSetters) { 408 throw new UnsupportedOperationException( 409 "TextureView doesn't support displaying a background drawable"); 410 } 411 } 412 413 /** 414 * Subclasses of TextureView cannot do their own rendering 415 * with the {@link Canvas} object. 416 * 417 * @param canvas The Canvas to which the View is rendered. 418 */ 419 @Override draw(Canvas canvas)420 public final void draw(Canvas canvas) { 421 // NOTE: Maintain this carefully (see View#draw) 422 mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; 423 424 /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background, 425 scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing 426 properties (alpha, layer paint) affect all of the content of a TextureView. */ 427 428 if (canvas.isHardwareAccelerated()) { 429 RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; 430 431 TextureLayer layer = getTextureLayer(); 432 if (layer != null) { 433 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "TextureView#draw()"); 434 applyUpdate(); 435 applyTransformMatrix(); 436 437 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date 438 recordingCanvas.drawTextureLayer(layer); 439 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 440 } 441 } 442 } 443 444 /** 445 * Subclasses of TextureView cannot do their own rendering 446 * with the {@link Canvas} object. 447 * 448 * @param canvas The Canvas to which the View is rendered. 449 */ 450 @Override onDraw(Canvas canvas)451 protected final void onDraw(Canvas canvas) { 452 } 453 454 @Override onSizeChanged(int w, int h, int oldw, int oldh)455 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 456 super.onSizeChanged(w, h, oldw, oldh); 457 if (mSurface != null) { 458 mSurface.setDefaultBufferSize(getWidth(), getHeight()); 459 updateLayer(); 460 if (mListener != null) { 461 mListener.onSurfaceTextureSizeChanged(mSurface, getWidth(), getHeight()); 462 } 463 } 464 } 465 getTextureLayer()466 TextureLayer getTextureLayer() { 467 if (mLayer == null) { 468 if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) { 469 return null; 470 } 471 472 mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer(); 473 boolean createNewSurface = (mSurface == null); 474 if (createNewSurface) { 475 // Create a new SurfaceTexture for the layer. 476 mSurface = new SurfaceTexture(false); 477 nCreateNativeWindow(mSurface); 478 } 479 mLayer.setSurfaceTexture(mSurface); 480 mSurface.setDefaultBufferSize(getWidth(), getHeight()); 481 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); 482 if (Flags.toolkitSetFrameRateReadOnly()) { 483 mSurface.setOnSetFrameRateListener( 484 (surfaceTexture, frameRate, compatibility, strategy) -> { 485 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 486 Trace.instant(Trace.TRACE_TAG_VIEW, "setFrameRate: " + frameRate); 487 } 488 setRequestedFrameRate(frameRate); 489 mFrameRateCompatibility = compatibility; 490 }, mAttachInfo.mHandler); 491 } 492 493 if (mListener != null && createNewSurface) { 494 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 495 } 496 mLayer.setLayerPaint(mLayerPaint); 497 } 498 499 if (mUpdateSurface) { 500 // Someone has requested that we use a specific SurfaceTexture, so 501 // tell mLayer about it and set the SurfaceTexture to use the 502 // current view size. 503 mUpdateSurface = false; 504 505 // Since we are updating the layer, force an update to ensure its 506 // parameters are correct (width, height, transform, etc.) 507 updateLayer(); 508 mMatrixChanged = true; 509 510 mLayer.setSurfaceTexture(mSurface); 511 mSurface.setDefaultBufferSize(getWidth(), getHeight()); 512 } 513 514 return mLayer; 515 } 516 517 @Override onVisibilityChanged(View changedView, int visibility)518 protected void onVisibilityChanged(View changedView, int visibility) { 519 super.onVisibilityChanged(changedView, visibility); 520 521 if (mSurface != null) { 522 // When the view becomes invisible, stop updating it, it's a waste of CPU 523 // To cancel updates, the easiest thing to do is simply to remove the 524 // updates listener 525 if (visibility == VISIBLE) { 526 if (mLayer != null) { 527 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); 528 } 529 updateLayerAndInvalidate(); 530 } else { 531 mSurface.setOnFrameAvailableListener(null); 532 } 533 } 534 } 535 updateLayer()536 private void updateLayer() { 537 synchronized (mLock) { 538 mUpdateLayer = true; 539 } 540 } 541 updateLayerAndInvalidate()542 private void updateLayerAndInvalidate() { 543 synchronized (mLock) { 544 mUpdateLayer = true; 545 } 546 invalidate(); 547 } 548 applyUpdate()549 private void applyUpdate() { 550 if (mLayer == null) { 551 return; 552 } 553 554 synchronized (mLock) { 555 if (mUpdateLayer) { 556 mUpdateLayer = false; 557 } else { 558 return; 559 } 560 } 561 562 mLayer.prepare(getWidth(), getHeight(), mOpaque); 563 mLayer.updateSurfaceTexture(); 564 565 if (mListener != null) { 566 mListener.onSurfaceTextureUpdated(mSurface); 567 } 568 } 569 570 /** 571 * <p>Sets the transform to associate with this texture view. 572 * The specified transform applies to the underlying surface 573 * texture and does not affect the size or position of the view 574 * itself, only of its content.</p> 575 * 576 * <p>Some transforms might prevent the content from drawing 577 * all the pixels contained within this view's bounds. In such 578 * situations, make sure this texture view is not marked opaque.</p> 579 * 580 * @param transform The transform to apply to the content of 581 * this view. If null the transform will be set to identity. 582 * 583 * @see #getTransform(android.graphics.Matrix) 584 * @see #isOpaque() 585 * @see #setOpaque(boolean) 586 */ setTransform(@ullable Matrix transform)587 public void setTransform(@Nullable Matrix transform) { 588 mMatrix.set(transform); 589 mMatrixChanged = true; 590 invalidateParentIfNeeded(); 591 } 592 593 /** 594 * Returns the transform associated with this texture view. 595 * 596 * @param transform The {@link Matrix} in which to copy the current 597 * transform. Can be null. 598 * 599 * @return The specified matrix if not null or a new {@link Matrix} 600 * instance otherwise. 601 * 602 * @see #setTransform(android.graphics.Matrix) 603 */ getTransform(@ullable Matrix transform)604 public @NonNull Matrix getTransform(@Nullable Matrix transform) { 605 if (transform == null) { 606 transform = new Matrix(); 607 } 608 609 transform.set(mMatrix); 610 611 return transform; 612 } 613 applyTransformMatrix()614 private void applyTransformMatrix() { 615 if (mMatrixChanged && mLayer != null) { 616 mLayer.setTransform(mMatrix); 617 mMatrixChanged = false; 618 } 619 } 620 621 /** 622 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 623 * of the associated surface texture. If the surface texture is not available, 624 * this method returns null.</p> 625 * 626 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 627 * pixel format and its dimensions are the same as this view's.</p> 628 * 629 * <p><strong>Do not</strong> invoke this method from a drawing method 630 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 631 * 632 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 633 * 634 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 635 * texture is not available or the width <= 0 or the height <= 0 636 * 637 * @see #isAvailable() 638 * @see #getBitmap(android.graphics.Bitmap) 639 * @see #getBitmap(int, int) 640 */ getBitmap()641 public @Nullable Bitmap getBitmap() { 642 return getBitmap(getWidth(), getHeight()); 643 } 644 645 /** 646 * <p>Returns a {@link android.graphics.Bitmap} representation of the content 647 * of the associated surface texture. If the surface texture is not available, 648 * this method returns null.</p> 649 * 650 * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} 651 * pixel format.</p> 652 * 653 * <p><strong>Do not</strong> invoke this method from a drawing method 654 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 655 * 656 * <p>If an error occurs during the copy, an empty bitmap will be returned.</p> 657 * 658 * @param width The width of the bitmap to create 659 * @param height The height of the bitmap to create 660 * 661 * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface 662 * texture is not available or width is <= 0 or height is <= 0 663 * 664 * @see #isAvailable() 665 * @see #getBitmap(android.graphics.Bitmap) 666 * @see #getBitmap() 667 */ getBitmap(int width, int height)668 public @Nullable Bitmap getBitmap(int width, int height) { 669 if (isAvailable() && width > 0 && height > 0) { 670 return getBitmap(Bitmap.createBitmap(getResources().getDisplayMetrics(), 671 width, height, Bitmap.Config.ARGB_8888)); 672 } 673 return null; 674 } 675 676 /** 677 * <p>Copies the content of this view's surface texture into the specified 678 * bitmap. If the surface texture is not available, the copy is not executed. 679 * The content of the surface texture will be scaled to fit exactly inside 680 * the specified bitmap.</p> 681 * 682 * <p><strong>Do not</strong> invoke this method from a drawing method 683 * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> 684 * 685 * <p>If an error occurs, the bitmap is left unchanged.</p> 686 * 687 * @param bitmap The bitmap to copy the content of the surface texture into, 688 * cannot be null, all configurations are supported 689 * 690 * @return The bitmap specified as a parameter 691 * 692 * @see #isAvailable() 693 * @see #getBitmap(int, int) 694 * @see #getBitmap() 695 * 696 * @throws IllegalStateException if the hardware rendering context cannot be 697 * acquired to capture the bitmap 698 */ getBitmap(@onNull Bitmap bitmap)699 public @NonNull Bitmap getBitmap(@NonNull Bitmap bitmap) { 700 if (bitmap != null && isAvailable()) { 701 applyUpdate(); 702 applyTransformMatrix(); 703 704 // This case can happen if the app invokes setSurfaceTexture() before 705 // we are able to create the hardware layer. We can safely initialize 706 // the layer here thanks to the validate() call at the beginning of 707 // this method 708 if (mLayer == null && mUpdateSurface) { 709 getTextureLayer(); 710 } 711 712 if (mLayer != null) { 713 mLayer.copyInto(bitmap); 714 } 715 } 716 return bitmap; 717 } 718 719 /** 720 * Returns true if the {@link SurfaceTexture} associated with this 721 * TextureView is available for rendering. When this method returns 722 * true, {@link #getSurfaceTexture()} returns a valid surface texture. 723 */ isAvailable()724 public boolean isAvailable() { 725 return mSurface != null; 726 } 727 728 /** 729 * <p>Start editing the pixels in the surface. The returned Canvas can be used 730 * to draw into the surface's bitmap. A null is returned if the surface has 731 * not been created or otherwise cannot be edited. You will usually need 732 * to implement 733 * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)} 734 * to find out when the Surface is available for use.</p> 735 * 736 * <p>The content of the Surface is never preserved between unlockCanvas() 737 * and lockCanvas(), for this reason, every pixel within the Surface area 738 * must be written. The only exception to this rule is when a dirty 739 * rectangle is specified, in which case, non-dirty pixels will be 740 * preserved.</p> 741 * 742 * <p>This method can only be used if the underlying surface is not already 743 * owned by another producer. For instance, if the TextureView is being used 744 * to render the camera's preview you cannot invoke this method.</p> 745 * 746 * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for 747 * drawing (see {@link #isAvailable()}). 748 * 749 * @see #lockCanvas(android.graphics.Rect) 750 * @see #unlockCanvasAndPost(android.graphics.Canvas) 751 */ lockCanvas()752 public @Nullable Canvas lockCanvas() { 753 return lockCanvas(null); 754 } 755 756 /** 757 * Just like {@link #lockCanvas()} but allows specification of a dirty 758 * rectangle. Every pixel within that rectangle must be written; however 759 * pixels outside the dirty rectangle will be preserved by the next call 760 * to lockCanvas(). 761 * 762 * This method can return null if the underlying surface texture is not 763 * available (see {@link #isAvailable()} or if the surface texture is 764 * already connected to an image producer (for instance: the camera, 765 * OpenGL, a media player, etc.) 766 * 767 * @param dirty Area of the surface that will be modified. If null the area of the entire 768 * surface is used. 769 770 * @return A Canvas used to draw into the surface, or null if the surface cannot be locked for 771 * drawing (see {@link #isAvailable()}). 772 * 773 * @see #lockCanvas() 774 * @see #unlockCanvasAndPost(android.graphics.Canvas) 775 * @see #isAvailable() 776 */ lockCanvas(@ullable Rect dirty)777 public @Nullable Canvas lockCanvas(@Nullable Rect dirty) { 778 if (!isAvailable()) return null; 779 780 if (mCanvas == null) { 781 mCanvas = new Canvas(); 782 } 783 784 synchronized (mNativeWindowLock) { 785 if (!nLockCanvas(mNativeWindow, mCanvas, dirty)) { 786 return null; 787 } 788 } 789 mSaveCount = mCanvas.save(); 790 791 return mCanvas; 792 } 793 794 /** 795 * Finish editing pixels in the surface. After this call, the surface's 796 * current pixels will be shown on the screen, but its content is lost, 797 * in particular there is no guarantee that the content of the Surface 798 * will remain unchanged when lockCanvas() is called again. 799 * 800 * @param canvas The Canvas previously returned by lockCanvas() 801 * 802 * @see #lockCanvas() 803 * @see #lockCanvas(android.graphics.Rect) 804 */ unlockCanvasAndPost(@onNull Canvas canvas)805 public void unlockCanvasAndPost(@NonNull Canvas canvas) { 806 if (mCanvas != null && canvas == mCanvas) { 807 canvas.restoreToCount(mSaveCount); 808 mSaveCount = 0; 809 810 synchronized (mNativeWindowLock) { 811 nUnlockCanvasAndPost(mNativeWindow, mCanvas); 812 } 813 } 814 } 815 816 /** 817 * Returns the {@link SurfaceTexture} used by this view. This method 818 * may return null if the view is not attached to a window or if the surface 819 * texture has not been initialized yet. 820 * 821 * @see #isAvailable() 822 */ getSurfaceTexture()823 public @Nullable SurfaceTexture getSurfaceTexture() { 824 return mSurface; 825 } 826 827 /** 828 * Set the {@link SurfaceTexture} for this view to use. If a {@link 829 * SurfaceTexture} is already being used by this view, it is immediately 830 * released and not usable any more. The {@link 831 * SurfaceTextureListener#onSurfaceTextureDestroyed} callback is <b>not</b> 832 * called for the previous {@link SurfaceTexture}. Similarly, the {@link 833 * SurfaceTextureListener#onSurfaceTextureAvailable} callback is <b>not</b> 834 * called for the {@link SurfaceTexture} passed to setSurfaceTexture. 835 * 836 * The {@link SurfaceTexture} object must be detached from all OpenGL ES 837 * contexts prior to calling this method. 838 * 839 * @param surfaceTexture The {@link SurfaceTexture} that the view should use. 840 * @see SurfaceTexture#detachFromGLContext() 841 */ setSurfaceTexture(@onNull SurfaceTexture surfaceTexture)842 public void setSurfaceTexture(@NonNull SurfaceTexture surfaceTexture) { 843 if (surfaceTexture == null) { 844 throw new NullPointerException("surfaceTexture must not be null"); 845 } 846 if (surfaceTexture == mSurface) { 847 throw new IllegalArgumentException("Trying to setSurfaceTexture to " + 848 "the same SurfaceTexture that's already set."); 849 } 850 if (surfaceTexture.isReleased()) { 851 throw new IllegalArgumentException("Cannot setSurfaceTexture to a " + 852 "released SurfaceTexture"); 853 } 854 if (mSurface != null) { 855 nDestroyNativeWindow(); 856 mSurface.release(); 857 } 858 mSurface = surfaceTexture; 859 nCreateNativeWindow(mSurface); 860 861 /* 862 * If the view is visible and we already made a layer, update the 863 * listener in the new surface to use the existing listener in the view. 864 * Otherwise this will be called when the view becomes visible or the 865 * layer is created 866 */ 867 if (((mViewFlags & VISIBILITY_MASK) == VISIBLE) && mLayer != null) { 868 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); 869 } 870 mUpdateSurface = true; 871 invalidateParentIfNeeded(); 872 } 873 874 /** 875 * Returns the {@link SurfaceTextureListener} currently associated with this 876 * texture view. 877 * 878 * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) 879 * @see SurfaceTextureListener 880 */ getSurfaceTextureListener()881 public @Nullable SurfaceTextureListener getSurfaceTextureListener() { 882 return mListener; 883 } 884 885 /** 886 * Sets the {@link SurfaceTextureListener} used to listen to surface 887 * texture events. 888 * 889 * @see #getSurfaceTextureListener() 890 * @see SurfaceTextureListener 891 */ setSurfaceTextureListener(@ullable SurfaceTextureListener listener)892 public void setSurfaceTextureListener(@Nullable SurfaceTextureListener listener) { 893 mListener = listener; 894 } 895 896 /** 897 * @hide 898 */ 899 @Override calculateFrameRateCategory()900 protected int calculateFrameRateCategory() { 901 long now = getDrawingTime(); 902 // This isn't necessary when the default frame rate is NORMAL (b/329156944) 903 if (mMinusTwoFrameIntervalMillis > 15 && (now - mLastFrameTimeMillis) > 15) { 904 return FRAME_RATE_CATEGORY_NORMAL; 905 } 906 return super.calculateFrameRateCategory(); 907 } 908 909 /** 910 * @hide 911 */ 912 @Override votePreferredFrameRate()913 protected void votePreferredFrameRate() { 914 super.votePreferredFrameRate(); 915 // This isn't necessary when the default frame rate is NORMAL (b/329156944) 916 long now = getDrawingTime(); 917 mMinusTwoFrameIntervalMillis = now - mLastFrameTimeMillis; 918 mLastFrameTimeMillis = now; 919 } 920 921 @UnsupportedAppUsage 922 private final SurfaceTexture.OnFrameAvailableListener mUpdateListener = 923 surfaceTexture -> { 924 updateLayer(); 925 invalidate(); 926 }; 927 928 /** 929 * This listener can be used to be notified when the surface texture 930 * associated with this texture view is available. 931 */ 932 public interface SurfaceTextureListener { 933 /** 934 * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. 935 * 936 * @param surface The surface returned by 937 * {@link android.view.TextureView#getSurfaceTexture()} 938 * @param width The width of the surface 939 * @param height The height of the surface 940 */ onSurfaceTextureAvailable(@onNull SurfaceTexture surface, int width, int height)941 void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height); 942 943 /** 944 * Invoked when the {@link SurfaceTexture}'s buffers size changed. 945 * 946 * @param surface The surface returned by 947 * {@link android.view.TextureView#getSurfaceTexture()} 948 * @param width The new width of the surface 949 * @param height The new height of the surface 950 */ onSurfaceTextureSizeChanged(@onNull SurfaceTexture surface, int width, int height)951 void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height); 952 953 /** 954 * Invoked when the specified {@link SurfaceTexture} is about to be destroyed. 955 * If returns true, no rendering should happen inside the surface texture after this method 956 * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}. 957 * Most applications should return true. 958 * 959 * @param surface The surface about to be destroyed 960 */ onSurfaceTextureDestroyed(@onNull SurfaceTexture surface)961 boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface); 962 963 /** 964 * Invoked when the specified {@link SurfaceTexture} is updated through 965 * {@link SurfaceTexture#updateTexImage()}. 966 * 967 * @param surface The surface just updated 968 */ onSurfaceTextureUpdated(@onNull SurfaceTexture surface)969 void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface); 970 } 971 972 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) nCreateNativeWindow(SurfaceTexture surface)973 private native void nCreateNativeWindow(SurfaceTexture surface); 974 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) nDestroyNativeWindow()975 private native void nDestroyNativeWindow(); 976 nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty)977 private static native boolean nLockCanvas(long nativeWindow, Canvas canvas, Rect dirty); nUnlockCanvasAndPost(long nativeWindow, Canvas canvas)978 private static native void nUnlockCanvasAndPost(long nativeWindow, Canvas canvas); 979 } 980