1 /* 2 * Copyright (C) 2019 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.settings.accessibility; 18 19 import android.content.Context; 20 import android.graphics.SurfaceTexture; 21 import android.media.MediaPlayer; 22 import android.view.Surface; 23 import android.view.TextureView; 24 import android.view.TextureView.SurfaceTextureListener; 25 26 import androidx.annotation.GuardedBy; 27 import androidx.annotation.RawRes; 28 import androidx.annotation.VisibleForTesting; 29 30 /** 31 * Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int, 32 * TextureView)} to setup the listener for TextureView and start to play the video. Once this player 33 * is no longer used, call {@link #release()} so that MediaPlayer object can be released. 34 */ 35 public class VideoPlayer implements SurfaceTextureListener { 36 private final Context mContext; 37 private final Object mMediaPlayerLock = new Object(); 38 // Media player object can't be used after it has been released, so it will be set to null. But 39 // VideoPlayer is asynchronized, media player object might be paused or resumed again before 40 // released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by 41 // mediaPlayerLock keep their states consistent. 42 @VisibleForTesting 43 @GuardedBy("mediaPlayerLock") 44 MediaPlayer mMediaPlayer; 45 46 @VisibleForTesting 47 @GuardedBy("mediaPlayerLock") 48 State mMediaPlayerState = State.NONE; 49 50 @RawRes 51 private final int mVideoRes; 52 53 @VisibleForTesting 54 Surface mAnimationSurface; 55 56 57 /** 58 * Creates a {@link MediaPlayer} for a given resource id and starts playback when the surface 59 * for 60 * a given {@link TextureView} is ready. 61 */ create(Context context, @RawRes int videoRes, TextureView textureView)62 public static VideoPlayer create(Context context, @RawRes int videoRes, 63 TextureView textureView) { 64 return new VideoPlayer(context, videoRes, textureView); 65 } 66 VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView)67 private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) { 68 this.mContext = context; 69 this.mVideoRes = videoRes; 70 textureView.setSurfaceTextureListener(this); 71 } 72 pause()73 public void pause() { 74 synchronized (mMediaPlayerLock) { 75 if (mMediaPlayerState == State.STARTED) { 76 mMediaPlayerState = State.PAUSED; 77 mMediaPlayer.pause(); 78 } 79 } 80 } 81 resume()82 public void resume() { 83 synchronized (mMediaPlayerLock) { 84 if (mMediaPlayerState == State.PAUSED) { 85 mMediaPlayer.start(); 86 mMediaPlayerState = State.STARTED; 87 } 88 } 89 } 90 91 /** Release media player when it's no longer needed. */ release()92 public void release() { 93 synchronized (mMediaPlayerLock) { 94 if (mMediaPlayerState != State.NONE && mMediaPlayerState != State.END) { 95 mMediaPlayerState = State.END; 96 mMediaPlayer.release(); 97 mMediaPlayer = null; 98 } 99 } 100 if (mAnimationSurface != null) { 101 mAnimationSurface.release(); 102 mAnimationSurface = null; 103 } 104 } 105 106 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)107 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 108 mAnimationSurface = new Surface(surface); 109 synchronized (mMediaPlayerLock) { 110 mMediaPlayer = MediaPlayer.create(mContext, mVideoRes); 111 mMediaPlayerState = State.PREPARED; 112 mMediaPlayer.setSurface(mAnimationSurface); 113 mMediaPlayer.setLooping(true); 114 mMediaPlayer.start(); 115 mMediaPlayerState = State.STARTED; 116 } 117 } 118 119 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)120 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 121 } 122 123 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)124 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 125 release(); 126 return false; 127 } 128 129 @Override onSurfaceTextureUpdated(SurfaceTexture surface)130 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 131 } 132 133 /** 134 * The state of MediaPlayer object. Refer to 135 * https://developer.android.com/reference/android/media/MediaPlayer#StateDiagram. 136 */ 137 public enum State { 138 /** MediaPlayer objects has not be created. */ 139 NONE, 140 /** MediaPlayer objects is created by create() method. */ 141 PREPARED, 142 /** MediaPlayer is started. It can be paused by pause() method. */ 143 STARTED, 144 /** MediaPlayer object is paused. Calling start() to resume it. */ 145 PAUSED, 146 /** 147 * MediaPlayer object is stopped and cannot be started until calling prepare() or 148 * prepareAsync() 149 * methods. 150 */ 151 STOPPED, 152 /** MediaPlayer object is released. It cannot be used again. */ 153 END 154 } 155 } 156 157