//
// VideoView
//
//
//
package com.csq.thesceneryalong.ui.views.video;
import java.io.IOException;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import com.csq.thesceneryalong.utils.DebugLog;
import com.csq.thesceneryalong.utils.apiadapter.video.VideoPlayerListener;
/**
* api >= 14, 播放视频使用VideoView14(TextureView)
* @author csq
*
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class VideoView14 extends TextureView implements TextureView.SurfaceTextureListener {
private MediaPlayer mMediaPlayer;
// private float mVideoHeight;
// private float mVideoWidth;
private boolean mIsDataSourceSet;
private boolean mIsViewAvailable;
private boolean mIsVideoPrepared;
private boolean mIsPlayCalled;
private State mState;
public enum State {
UNINITIALIZED, PLAY, STOP, PAUSE, END
}
public VideoView14(Context context) {
super(context);
initView();
}
public VideoView14(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public VideoView14(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
mMediaPlayer.setSurface(surface);
mIsViewAvailable = true;
if (mIsDataSourceSet && mIsPlayCalled && mIsVideoPrepared) {
DebugLog.d("View is available and play() was called.");
play();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
private int lastProgress = 0;
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
int dur = mMediaPlayer.getDuration();
if(dur > 0 && mState != State.END){
int progress = mMediaPlayer.getCurrentPosition() * 100 / dur;
if(progress != lastProgress){
if(mListener != null){
mListener.onProgressChanged(progress);
lastProgress = progress;
}
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
if(mMediaPlayer == null){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
float mVideoWidth = mMediaPlayer.getVideoWidth();
float mVideoHeight = mMediaPlayer.getVideoHeight();
if(mVideoWidth > 0 && mVideoHeight > 0){
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
float scale = 1.0f;
if(mVideoWidth > 0 && mVideoHeight > 0
&& viewWidth > 0 && viewHeight > 0){
if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
//2边都超出,scale<1
scale = Math.min(viewWidth / mVideoWidth, viewHeight / mVideoHeight);
}else if(mVideoWidth < viewWidth && mVideoHeight < viewHeight){
//2边都小于,scale>1
scale = Math.min(viewWidth / mVideoWidth, viewHeight / mVideoHeight);
}else if(mVideoWidth >= viewWidth){
//宽度超出,scale<1
scale = viewWidth / mVideoWidth;
}else{
//高度超出,scale<1
scale = viewHeight / mVideoHeight;
}
}
int mx = (int) (mVideoWidth * scale);
int my = (int) (mVideoHeight * scale);
setMeasuredDimension(mx, my);
}else{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
//默认占满父空间
DebugLog.d("VideoView14 getMeasuredWidth = " + getMeasuredWidth());
DebugLog.d("VideoView14 getMeasuredHeight = " + getMeasuredHeight());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
if(w != oldw || h != oldh){
if(mListener != null){
mListener.onSizeChanged(getMeasuredWidth(), getMeasuredHeight());
}
}
}
private void initView() {
initPlayer();
setSurfaceTextureListener(this);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(mIsViewAvailable && mIsDataSourceSet && mIsVideoPrepared){
if(isPlaying()){
pause();
}else{
play();
}
}
}
});
}
private void initPlayer() {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
}
mIsVideoPrepared = false;
mIsPlayCalled = false;
mState = State.UNINITIALIZED;
}
private void prepare() {
try {
mMediaPlayer.setOnVideoSizeChangedListener(
new MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
//mVideoWidth = width;
//mVideoHeight = height;
requestLayout();
}
}
);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mState = State.END;
DebugLog.d("Video has ended.");
if (mListener != null) {
mListener.onVideoEnd();
}
}
});
// don't forget to call MediaPlayer.prepareAsync() method when you use constructor for
// creating MediaPlayer
mMediaPlayer.prepareAsync();
// Play video when the media source is ready for playback.
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mIsVideoPrepared = true;
if (mIsPlayCalled){
if(mIsViewAvailable){
DebugLog.d("Player is prepared and play() was called.");
play();
}
}else{
//没有开始播放,也跳到第一帧,显示画面
if(mIsViewAvailable){
seekTo(1);
}
}
if (mListener != null) {
mListener.onVideoPrepared();
}
}
});
} catch (IllegalArgumentException e) {
DebugLog.d( e.getMessage());
} catch (SecurityException e) {
DebugLog.d(e.getMessage());
} catch (IllegalStateException e) {
DebugLog.d(e.toString());
}
}
/*private void updateTextureViewSize() {
float viewWidth = getWidth();
float viewHeight = getHeight();
//scale相对于View来说,scale = 计算得到的视频大小/viewSize
float scale = 1.0f;
float scaleX = 1.0f;
float scaleY = 1.0f;
if(mVideoWidth > 0 && mVideoHeight > 0
&& viewWidth > 0 && viewHeight > 0){
if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
//2边都超出,scale<1
scale = Math.min(viewWidth / mVideoWidth, viewHeight / mVideoHeight);
}else if(mVideoWidth < viewWidth && mVideoHeight < viewHeight){
//2边都小于,scale>1
scale = Math.min(viewWidth / mVideoWidth, viewHeight / mVideoHeight);
}else if(mVideoWidth >= viewWidth){
//宽度超出,scale<1
scale = viewWidth / mVideoWidth;
}else{
//高度超出,scale<1
scale = viewHeight / mVideoHeight;
}
scaleX = mVideoWidth * scale / viewWidth;
scaleY = mVideoHeight * scale / viewHeight;
}
// Calculate pivot points, in our case crop from center
int pivotPointX;
int pivotPointY;
pivotPointX = (int) (viewWidth / 2);
pivotPointY = (int) (viewHeight / 2);
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);
setTransform(matrix);
}*/
/**
* @see android.media.MediaPlayer#setDataSource(String)
*/
public void setDataSource(String path) {
initPlayer();
try {
mMediaPlayer.setDataSource(path);
mIsDataSourceSet = true;
prepare();
} catch (IOException e) {
DebugLog.d(e.getMessage());
}
}
/**
* @see android.media.MediaPlayer#setDataSource(android.content.Context, android.net.Uri)
*/
public void setDataSource(Context context, Uri uri) {
initPlayer();
try {
mMediaPlayer.setDataSource(context, uri);
mIsDataSourceSet = true;
prepare();
} catch (IOException e) {
DebugLog.d(e.getMessage());
}
}
/**
* @see android.media.MediaPlayer#setDataSource(java.io.FileDescriptor)
*/
public void setDataSource(AssetFileDescriptor afd) {
initPlayer();
try {
long startOffset = afd.getStartOffset();
long length = afd.getLength();
mMediaPlayer.setDataSource(afd.getFileDescriptor(), startOffset, length);
mIsDataSourceSet = true;
prepare();
} catch (IOException e) {
DebugLog.d(e.getMessage());
}
}
/**
* Play or resume video. Video will be played as soon as view is available and media player is
* prepared.
*
* If video is stopped or ended and play() method was called, video will start over.
*/
public void play() {
if (!mIsDataSourceSet) {
DebugLog.d("play() was called but data source was not set.");
return;
}
mIsPlayCalled = true;
if (!mIsVideoPrepared) {
DebugLog.d("play() was called but video is not prepared yet, waiting.");
return;
}
if (!mIsViewAvailable) {
DebugLog.d("play() was called but view is not available yet, waiting.");
return;
}
if (mState == State.PLAY) {
DebugLog.d("play() was called but video is already playing.");
return;
}
if (mState == State.PAUSE) {
DebugLog.d("play() was called but video is paused, resuming.");
mState = State.PLAY;
mMediaPlayer.start();
if(mListener != null){
mListener.onVideoPlayed();
}
return;
}
if (mState == State.END || mState == State.STOP) {
DebugLog.d("play() was called but video already ended, starting over.");
mState = State.PLAY;
mMediaPlayer.seekTo(0);
mMediaPlayer.start();
if(mListener != null){
mListener.onVideoPlayed();
}
return;
}
mState = State.PLAY;
mMediaPlayer.start();
if(mListener != null){
mListener.onVideoPlayed();
}
}
public boolean isPlaying(){
if(mMediaPlayer != null){
try {
return mMediaPlayer.isPlaying();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
return false;
}
/**
* Pause video. If video is already paused, stopped or ended nothing will happen.
*/
public void pause() {
if (mState == State.PAUSE) {
DebugLog.d("pause() was called but video already paused.");
return;
}
if (mState == State.STOP) {
DebugLog.d("pause() was called but video already stopped.");
return;
}
if (mState == State.END) {
DebugLog.d("pause() was called but video already ended.");
return;
}
mState = State.PAUSE;
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
}
if(mListener != null){
mListener.onVideoPaused();
}
}
/**
* Stop video (pause and seek to beginning). If video is already stopped or ended nothing will
* happen.
*/
public void stop() {
if (mState == State.STOP) {
DebugLog.d("stop() was called but video already stopped.");
return;
}
if (mState == State.END) {
DebugLog.d("stop() was called but video already ended.");
return;
}
mState = State.STOP;
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
mMediaPlayer.seekTo(0);
}
if(mListener != null){
mListener.onVideoStoped();
}
}
public void release() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
mIsVideoPrepared = false;
mIsPlayCalled = false;
mState = State.UNINITIALIZED;
}
/**
* @see android.media.MediaPlayer#setLooping(boolean)
*/
public void setLooping(boolean looping) {
mMediaPlayer.setLooping(looping);
}
/**
* @see android.media.MediaPlayer#seekTo(int)
*/
public void seekTo(int milliseconds) {
mMediaPlayer.seekTo(milliseconds);
}
/**
* @see android.media.MediaPlayer#getDuration()
*/
public int getDuration() {
return mMediaPlayer.getDuration();
}
private VideoPlayerListener mListener;
/**
* Listener trigger 'onVideoPrepared' and `onVideoEnd` events
*/
public void setVideoListener(VideoPlayerListener listener) {
mListener = listener;
}
}