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