1 /* 2 * Copyright 2018 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.widget; 18 19 import android.content.Context; 20 import android.graphics.SurfaceTexture; 21 import android.media.MediaPlayer2; 22 import android.support.annotation.NonNull; 23 import android.support.annotation.RequiresApi; 24 import android.util.AttributeSet; 25 import android.util.Log; 26 import android.view.Surface; 27 import android.view.TextureView; 28 import android.view.View; 29 30 import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW; 31 32 @RequiresApi(26) 33 class VideoTextureView extends TextureView 34 implements VideoViewInterface, TextureView.SurfaceTextureListener { 35 private static final String TAG = "VideoTextureView"; 36 private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG); 37 38 private SurfaceTexture mSurfaceTexture; 39 private Surface mSurface; 40 private SurfaceListener mSurfaceListener; 41 private MediaPlayer2 mMediaPlayer; 42 // A flag to indicate taking over other view should be proceed. 43 private boolean mIsTakingOverOldView; 44 private VideoViewInterface mOldView; 45 VideoTextureView(Context context)46 public VideoTextureView(Context context) { 47 this(context, null); 48 } 49 VideoTextureView(Context context, AttributeSet attrs)50 public VideoTextureView(Context context, AttributeSet attrs) { 51 this(context, attrs, 0); 52 } 53 VideoTextureView(Context context, AttributeSet attrs, int defStyleAttr)54 public VideoTextureView(Context context, AttributeSet attrs, int defStyleAttr) { 55 this(context, attrs, defStyleAttr, 0); 56 } 57 VideoTextureView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)58 public VideoTextureView( 59 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 60 super(context, attrs, defStyleAttr, defStyleRes); 61 setSurfaceTextureListener(this); 62 } 63 64 //////////////////////////////////////////////////// 65 // implements VideoViewInterface 66 //////////////////////////////////////////////////// 67 68 @Override assignSurfaceToMediaPlayer(MediaPlayer2 mp)69 public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) { 70 Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture); 71 if (mp == null || !hasAvailableSurface()) { 72 // Surface is not ready. 73 return false; 74 } 75 mp.setSurface(mSurface); 76 return true; 77 } 78 79 @Override setSurfaceListener(SurfaceListener l)80 public void setSurfaceListener(SurfaceListener l) { 81 mSurfaceListener = l; 82 } 83 84 @Override getViewType()85 public int getViewType() { 86 return VIEW_TYPE_TEXTUREVIEW; 87 } 88 89 @Override setMediaPlayer(MediaPlayer2 mp)90 public void setMediaPlayer(MediaPlayer2 mp) { 91 mMediaPlayer = mp; 92 if (mIsTakingOverOldView) { 93 takeOver(mOldView); 94 } 95 } 96 97 @Override takeOver(@onNull VideoViewInterface oldView)98 public void takeOver(@NonNull VideoViewInterface oldView) { 99 if (assignSurfaceToMediaPlayer(mMediaPlayer)) { 100 ((View) oldView).setVisibility(GONE); 101 mIsTakingOverOldView = false; 102 mOldView = null; 103 if (mSurfaceListener != null) { 104 mSurfaceListener.onSurfaceTakeOverDone(this); 105 } 106 } else { 107 mIsTakingOverOldView = true; 108 mOldView = oldView; 109 } 110 } 111 112 @Override hasAvailableSurface()113 public boolean hasAvailableSurface() { 114 return (mSurfaceTexture != null && !mSurfaceTexture.isReleased() && mSurface != null); 115 } 116 117 //////////////////////////////////////////////////// 118 // implements TextureView.SurfaceTextureListener 119 //////////////////////////////////////////////////// 120 121 @Override onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height)122 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { 123 Log.d(TAG, "onSurfaceTextureAvailable: mSurfaceTexture: " + mSurfaceTexture 124 + ", new surface: " + surfaceTexture); 125 mSurfaceTexture = surfaceTexture; 126 mSurface = new Surface(mSurfaceTexture); 127 if (mIsTakingOverOldView) { 128 takeOver(mOldView); 129 } else { 130 assignSurfaceToMediaPlayer(mMediaPlayer); 131 } 132 if (mSurfaceListener != null) { 133 mSurfaceListener.onSurfaceCreated(this, width, height); 134 } 135 } 136 137 @Override onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height)138 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { 139 if (mSurfaceListener != null) { 140 mSurfaceListener.onSurfaceChanged(this, width, height); 141 } 142 // requestLayout(); // TODO: figure out if it should be called here? 143 } 144 145 @Override onSurfaceTextureUpdated(SurfaceTexture surface)146 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 147 // no-op 148 } 149 150 @Override onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture)151 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { 152 if (mSurfaceListener != null) { 153 mSurfaceListener.onSurfaceDestroyed(this); 154 } 155 mSurfaceTexture = null; 156 mSurface = null; 157 return true; 158 } 159 160 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)161 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 162 int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoWidth(); 163 int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoHeight(); 164 if (DEBUG) { 165 Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " 166 + MeasureSpec.toString(heightMeasureSpec) + ")"); 167 Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); 168 Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight()); 169 Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight); 170 } 171 172 int width = getDefaultSize(videoWidth, widthMeasureSpec); 173 int height = getDefaultSize(videoHeight, heightMeasureSpec); 174 175 if (videoWidth > 0 && videoHeight > 0) { 176 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 177 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 178 179 width = widthSpecSize; 180 height = heightSpecSize; 181 182 // for compatibility, we adjust size based on aspect ratio 183 if (videoWidth * height < width * videoHeight) { 184 width = height * videoWidth / videoHeight; 185 if (DEBUG) { 186 Log.d(TAG, "image too wide, correcting. width: " + width); 187 } 188 } else if (videoWidth * height > width * videoHeight) { 189 height = width * videoHeight / videoWidth; 190 if (DEBUG) { 191 Log.d(TAG, "image too tall, correcting. height: " + height); 192 } 193 } 194 } else { 195 // no size yet, just adopt the given spec sizes 196 } 197 setMeasuredDimension(width, height); 198 if (DEBUG) { 199 Log.i(TAG, "end of onMeasure()"); 200 Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); 201 } 202 } 203 204 @Override toString()205 public String toString() { 206 return "ViewType: TextureView / Visibility: " + getVisibility() 207 + " / surfaceTexture: " + mSurfaceTexture; 208 209 } 210 } 211