Home | History | Annotate | Download | only in impl
      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 
     48   public VideoSurfaceTextureImpl(boolean isPixel2017, @SurfaceType int surfaceType) {
     49     this.isPixel2017 = isPixel2017;
     50     this.surfaceType = surfaceType;
     51   }
     52 
     53   @Override
     54   public void setDelegate(VideoSurfaceDelegate delegate) {
     55     LogUtil.i("VideoSurfaceTextureImpl.setDelegate", "delegate: " + delegate + " " + toString());
     56     this.delegate = delegate;
     57   }
     58 
     59   @Override
     60   public int getSurfaceType() {
     61     return surfaceType;
     62   }
     63 
     64   @Override
     65   public Surface getSavedSurface() {
     66     return savedSurface;
     67   }
     68 
     69   @Override
     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
     88   public Point getSurfaceDimensions() {
     89     return surfaceDimensions;
     90   }
     91 
     92   @Override
     93   public void setSourceVideoDimensions(Point sourceVideoDimensions) {
     94     this.sourceVideoDimensions = sourceVideoDimensions;
     95   }
     96 
     97   @Override
     98   public Point getSourceVideoDimensions() {
     99     return sourceVideoDimensions;
    100   }
    101 
    102   @Override
    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
    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 
    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 
    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 
    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
    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
    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
    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
    244     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
    245 
    246     @Override
    247     public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
    248   }
    249 
    250   private class OnClickListener implements View.OnClickListener {
    251     @Override
    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