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