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