1 /* 2 * Copyright (C) 2014 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 com.android.incallui.videosurface.impl; 18 19 import android.graphics.Point; 20 import android.graphics.SurfaceTexture; 21 import android.os.Build.VERSION; 22 import android.os.Build.VERSION_CODES; 23 import android.view.Surface; 24 import android.view.TextureView; 25 import android.view.View; 26 import com.android.dialer.common.LogUtil; 27 import com.android.incallui.videosurface.protocol.VideoSurfaceDelegate; 28 import com.android.incallui.videosurface.protocol.VideoSurfaceTexture; 29 import java.util.Locale; 30 import java.util.Objects; 31 32 /** 33 * Represents a {@link TextureView} and its associated {@link SurfaceTexture} and {@link Surface}. 34 * Used to manage the lifecycle of these objects across device orientation changes. 35 */ 36 public class VideoSurfaceTextureImpl implements VideoSurfaceTexture { 37 @SurfaceType private final int surfaceType; 38 private final boolean isPixel2017; 39 40 private VideoSurfaceDelegate delegate; 41 private TextureView textureView; 42 private Surface savedSurface; 43 private SurfaceTexture savedSurfaceTexture; 44 private Point surfaceDimensions; 45 private Point sourceVideoDimensions; 46 private boolean isDoneWithSurface; 47 VideoSurfaceTextureImpl(boolean isPixel2017, @SurfaceType int surfaceType)48 public VideoSurfaceTextureImpl(boolean isPixel2017, @SurfaceType int surfaceType) { 49 this.isPixel2017 = isPixel2017; 50 this.surfaceType = surfaceType; 51 } 52 53 @Override setDelegate(VideoSurfaceDelegate delegate)54 public void setDelegate(VideoSurfaceDelegate delegate) { 55 LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString()); 56 this.delegate = delegate; 57 } 58 59 @Override getSurfaceType()60 public int getSurfaceType() { 61 return surfaceType; 62 } 63 64 @Override getSavedSurface()65 public Surface getSavedSurface() { 66 return savedSurface; 67 } 68 69 @Override setSurfaceDimensions(Point surfaceDimensions)70 public void setSurfaceDimensions(Point surfaceDimensions) { 71 LogUtil.i( 72 "VideoSurfaceTextureImpl.setSurfaceDimensions", 73 "surfaceDimensions: " + surfaceDimensions + " " + toString()); 74 this.surfaceDimensions = surfaceDimensions; 75 if (surfaceDimensions != null && savedSurfaceTexture != null) { 76 // Only do this on O (not at least O) because we expect this issue to be fixed in OMR1 77 if (VERSION.SDK_INT == VERSION_CODES.O && isPixel2017) { 78 LogUtil.i( 79 "VideoSurfaceTextureImpl.setSurfaceDimensions", 80 "skip setting default buffer size on Pixel 2017 ODR"); 81 return; 82 } 83 savedSurfaceTexture.setDefaultBufferSize(surfaceDimensions.x, surfaceDimensions.y); 84 } 85 } 86 87 @Override getSurfaceDimensions()88 public Point getSurfaceDimensions() { 89 return surfaceDimensions; 90 } 91 92 @Override setSourceVideoDimensions(Point sourceVideoDimensions)93 public void setSourceVideoDimensions(Point sourceVideoDimensions) { 94 this.sourceVideoDimensions = sourceVideoDimensions; 95 } 96 97 @Override getSourceVideoDimensions()98 public Point getSourceVideoDimensions() { 99 return sourceVideoDimensions; 100 } 101 102 @Override attachToTextureView(TextureView textureView)103 public void attachToTextureView(TextureView textureView) { 104 if (this.textureView == textureView) { 105 return; 106 } 107 LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", toString()); 108 109 if (this.textureView != null) { 110 this.textureView.setOnClickListener(null); 111 this.textureView.setSurfaceTextureListener(null); 112 } 113 114 this.textureView = textureView; 115 textureView.setSurfaceTextureListener(new SurfaceTextureListener()); 116 textureView.setOnClickListener(new OnClickListener()); 117 118 boolean areSameSurfaces = Objects.equals(savedSurfaceTexture, textureView.getSurfaceTexture()); 119 LogUtil.i("VideoSurfaceTextureImpl.attachToTextureView", "areSameSurfaces: " + areSameSurfaces); 120 if (savedSurfaceTexture != null && !areSameSurfaces) { 121 textureView.setSurfaceTexture(savedSurfaceTexture); 122 if (surfaceDimensions != null && createSurface(surfaceDimensions.x, surfaceDimensions.y)) { 123 onSurfaceCreated(); 124 } 125 } 126 isDoneWithSurface = false; 127 } 128 129 @Override setDoneWithSurface()130 public void setDoneWithSurface() { 131 LogUtil.i("VideoSurfaceTextureImpl.setDoneWithSurface", toString()); 132 isDoneWithSurface = true; 133 if (textureView != null && textureView.isAvailable()) { 134 return; 135 } 136 if (savedSurface != null) { 137 onSurfaceReleased(); 138 savedSurface.release(); 139 savedSurface = null; 140 } 141 if (savedSurfaceTexture != null) { 142 savedSurfaceTexture.release(); 143 savedSurfaceTexture = null; 144 } 145 } 146 createSurface(int width, int height)147 private boolean createSurface(int width, int height) { 148 LogUtil.i( 149 "VideoSurfaceTextureImpl.createSurface", 150 "width: " + width + ", height: " + height + " " + toString()); 151 savedSurfaceTexture.setDefaultBufferSize(width, height); 152 if (savedSurface != null) { 153 savedSurface.release(); 154 } 155 savedSurface = new Surface(savedSurfaceTexture); 156 return true; 157 } 158 onSurfaceCreated()159 private void onSurfaceCreated() { 160 if (delegate != null) { 161 delegate.onSurfaceCreated(this); 162 } else { 163 LogUtil.e("VideoSurfaceTextureImpl.onSurfaceCreated", "delegate is null. " + toString()); 164 } 165 } 166 onSurfaceReleased()167 private void onSurfaceReleased() { 168 if (delegate != null) { 169 delegate.onSurfaceReleased(this); 170 } else { 171 LogUtil.e("VideoSurfaceTextureImpl.onSurfaceReleased", "delegate is null. " + toString()); 172 } 173 } 174 175 @Override toString()176 public String toString() { 177 return String.format( 178 Locale.US, 179 "VideoSurfaceTextureImpl<%s%s%s%s>", 180 (surfaceType == SURFACE_TYPE_LOCAL ? "local, " : "remote, "), 181 (savedSurface == null ? "no-surface, " : ""), 182 (savedSurfaceTexture == null ? "no-texture, " : ""), 183 (surfaceDimensions == null 184 ? "(-1 x -1)" 185 : (surfaceDimensions.x + " x " + surfaceDimensions.y))); 186 } 187 188 private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { 189 @Override onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height)190 public void onSurfaceTextureAvailable(SurfaceTexture newSurfaceTexture, int width, int height) { 191 LogUtil.i( 192 "SurfaceTextureListener.onSurfaceTextureAvailable", 193 "newSurfaceTexture: " 194 + newSurfaceTexture 195 + " " 196 + VideoSurfaceTextureImpl.this.toString()); 197 198 // Where there is no saved {@link SurfaceTexture} available, use the newly created one. 199 // If a saved {@link SurfaceTexture} is available, we are re-creating after an 200 // orientation change. 201 boolean surfaceCreated; 202 if (savedSurfaceTexture == null) { 203 savedSurfaceTexture = newSurfaceTexture; 204 surfaceCreated = createSurface(width, height); 205 } else { 206 // A saved SurfaceTexture was found. 207 LogUtil.i( 208 "SurfaceTextureListener.onSurfaceTextureAvailable", "replacing with cached surface..."); 209 textureView.setSurfaceTexture(savedSurfaceTexture); 210 surfaceCreated = true; 211 } 212 213 // Inform the delegate that the surface is available. 214 if (surfaceCreated) { 215 onSurfaceCreated(); 216 } 217 } 218 219 @Override onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture)220 public boolean onSurfaceTextureDestroyed(SurfaceTexture destroyedSurfaceTexture) { 221 LogUtil.i( 222 "SurfaceTextureListener.onSurfaceTextureDestroyed", 223 "destroyedSurfaceTexture: %s, %s, isDoneWithSurface: %b", 224 destroyedSurfaceTexture, 225 VideoSurfaceTextureImpl.this.toString(), 226 isDoneWithSurface); 227 if (delegate != null) { 228 delegate.onSurfaceDestroyed(VideoSurfaceTextureImpl.this); 229 } else { 230 LogUtil.e("SurfaceTextureListener.onSurfaceTextureDestroyed", "delegate is null"); 231 } 232 233 if (isDoneWithSurface) { 234 onSurfaceReleased(); 235 if (savedSurface != null) { 236 savedSurface.release(); 237 savedSurface = null; 238 } 239 } 240 return isDoneWithSurface; 241 } 242 243 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)244 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} 245 246 @Override onSurfaceTextureUpdated(SurfaceTexture surface)247 public void onSurfaceTextureUpdated(SurfaceTexture surface) {} 248 } 249 250 private class OnClickListener implements View.OnClickListener { 251 @Override onClick(View view)252 public void onClick(View view) { 253 if (delegate != null) { 254 delegate.onSurfaceClick(VideoSurfaceTextureImpl.this); 255 } else { 256 LogUtil.e("OnClickListener.onClick", "delegate is null"); 257 } 258 } 259 } 260 } 261