MediaPlay+TextureView - 视频播放小控件
一、主要功能:
简化了视频播放的调用,并采用SurfaceTexture改善了播放效率.
1、播放器的初始化和释放,
2、播放器和界面自动绑定, 简化调用
3、整合播放异常和信息回馈,统一回调处理
4、集成网络URL,本地文件,本地asset播放功能.
5、播放流程自动化,一个接口实现所有功能.
6、 采用SurfaceTexture渲染,节省资源
二、使用步骤
在xml文件应用 或者 class文件直接new
<ELiveTextureView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
调用并设置播放文件
elive_main_video.setVideoURI(getResources().openRawResourceFd(R.raw.main_bg));
三、API接口说明
- setVideoURI(Uri uri) 设置网络视频和本地SD文件地址
- setVideoURI(AssetFileDescriptor fileDescriptor) 设置asset类型的视频文件路径
- stopPlayback() 停止播放并释放播放器资源
- getDuration() 获取视频的总长度,无视频返回0
- getCurrentPosition() 获取当前播放的视频进度,无播放返回0
- seekTo(int msec) 设置视频跳转进度,如果未播放则设置初始播放位置
- start() 继续播放
- pause() 暂停播放
- isPlaying() 判断是否在播放
- setLooping(boolean looping) 设置是否循环播放
- setCompletionListener(MediaPlayer.OnCompletionListener mCompletionListener) 设置监听器, 监听视频是否播放完成, 循环播放一次回调一次
- setOnInfoListener(MediaPlayer.OnInfoListener mInfoListener) 设置监听,监听所有播放信息,包括error,info,自定义信息
四、源码
package portal.ui.widget;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;
/**
* 播放视频的控件
*/
public class ELiveTextureView extends TextureView {
private final String TAG = this.getClass().getSimpleName();
private Context mContext;
private Uri mUri;
private AssetFileDescriptor fileDescriptor;
private MediaPlayer mMediaPlayer = null;
private boolean mIsPrepared;
private int mSurfaceWidth,mSurfaceHeight;
private int mSeekPos;
private boolean isLooping = false;// 是否循环播放
private View thisView = this;
private static int errorCount; //重试次数
private final static Object mLockObject = new Object();
private static final int INIT_PLAYER = 10000;
private static final int START_PLAY_VIDEO = 10002;
private static final int STOP_PLAY_VIDEO = 10003;
private static final int UPDATA_PLAY_INFO = 10004;
private Handler myHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case INIT_PLAYER:
LogUtil.d_3(TAG, "init player");
synchronized(mLockObject) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setLooping(isLooping);
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mMediaPlayerErrorListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangeListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setSurface(new Surface(mSurfaceTexture));
if(mUri != null || fileDescriptor != null){
myHandler.sendEmptyMessage(START_PLAY_VIDEO);
}
}
}
break;
case START_PLAY_VIDEO:
synchronized(mLockObject) {
try {
if (mMediaPlayer != null) {
if (mUri != null) {
mMediaPlayer.setDataSource(mContext, mUri);
} else if (fileDescriptor != null) {
mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),fileDescriptor.getLength());
}
mMediaPlayer.prepareAsync();
}
} catch (Exception ex) {
LogUtil.d_e(TAG, ex);
ex.printStackTrace();
}
}
break;
case STOP_PLAY_VIDEO:
synchronized(mLockObject) {
if(mMediaPlayer != null) {
LogUtil.d_3(TAG, " release player");
mIsPrepared = false;
try {
mMediaPlayer.stop();
mMediaPlayer.reset();
}catch (Exception ex){
LogUtil.d_e(TAG, ex);
ex.printStackTrace();
}
}
}
break;
case UPDATA_PLAY_INFO:
myHandler.sendEmptyMessageDelayed(UPDATA_PLAY_INFO,1000);
break;
}
return false;
}
});
public ELiveTextureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ELiveTextureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
initVideoView();
}
private SurfaceTexture mSurfaceTexture;
private void initVideoView() {
LogUtil.d_3(TAG, "initVideoView");
setSurfaceTextureListener(new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
if (mSurfaceTexture == null) {
mSurfaceTexture = surface;
myHandler.sendEmptyMessage(INIT_PLAYER);
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
myHandler.sendEmptyMessage(STOP_PLAY_VIDEO);
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 这个之前是默认的拉伸图像
if (mSurfaceWidth != 0 && mSurfaceHeight != 0) {
setMeasuredDimension(mSurfaceWidth, mSurfaceHeight);
}
invalidate();
}
/**
* 开始播放视频
*/
public void setVideoURI(Uri uri) {
LogUtil.d_3(TAG, " setVideoURI->uri = "+uri.toString());
this.mUri = uri;
this.fileDescriptor = null;
mSeekPos = 0;
errorCount = 0;
myHandler.sendEmptyMessage(START_PLAY_VIDEO);
}
/**ProgressBar_video
* 开始播放视频2
*/
public void setVideoURI(AssetFileDescriptor fileDescriptor) {
LogUtil.d_3(TAG, " setVideoURI->uri = "+ fileDescriptor.toString());
this.fileDescriptor = fileDescriptor;
this.mUri = null;
mSeekPos = 0;
errorCount = 0;
myHandler.sendEmptyMessage(START_PLAY_VIDEO);
}
/**
* 停止并释放资源
*/
public void stopPlayback() {
LogUtil.d_3(TAG, " stopPlay");
myHandler.removeCallbacksAndMessages(null);
myHandler.sendEmptyMessage(STOP_PLAY_VIDEO);
}
/**
* 获取视频长度
*/
public long getDuration() {
if (mMediaPlayer != null && mIsPrepared) {
return mMediaPlayer.getDuration();
}
return 0;
}
/**
* 获取视频当前播放位置
*/
public int getCurrentPosition() {
if (mMediaPlayer != null && mIsPrepared) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
/**
* 设置视频播放位置
*/
public void seekTo(int msec) {
mSeekPos = msec;
if (mMediaPlayer != null && mIsPrepared) {
mMediaPlayer.seekTo(msec);
}
}
/**
* 恢复播放
*/
public void start(){
if (mMediaPlayer != null && mIsPrepared) {
mMediaPlayer.start();
}
}
/**
* 暂停播放
*/
public void pause(){
if (mMediaPlayer != null && mIsPrepared) {
mMediaPlayer.pause();
}
}
/**
* 获取播放状态
*/
public boolean isPlaying() {
if (mMediaPlayer != null && mIsPrepared) {
return mMediaPlayer.isPlaying();
}
return false;
}
/**
* 设置是否循环播放
*/
public void setLooping(boolean looping) {
isLooping = looping;
if(mMediaPlayer != null){
mMediaPlayer.setLooping(isLooping);
}
}
private MediaPlayer.OnCompletionListener mCompletionListener;
public void setCompletionListener(MediaPlayer.OnCompletionListener mCompletionListener){
this.mCompletionListener = mCompletionListener;
if(mMediaPlayer != null){
mMediaPlayer.setOnCompletionListener(mCompletionListener);
}
}
public final static int ELIVE_INFO_START = 10000;
private MediaPlayer.OnInfoListener mInfoListener;
public void setOnInfoListener(MediaPlayer.OnInfoListener mInfoListener){
this.mInfoListener = mInfoListener;
if(mMediaPlayer != null){
mMediaPlayer.setOnInfoListener(mInfoListener);
}
}
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
synchronized(mLockObject) {
LogUtil.d_3(TAG, " mPreparedListener onPrepared");
mIsPrepared = true;
mMediaPlayer.seekTo(mSeekPos);
mMediaPlayer.start();
if(mInfoListener != null){
mInfoListener.onInfo(mMediaPlayer,ELIVE_INFO_START,0);
}
}
}
};
private MediaPlayer.OnErrorListener mMediaPlayerErrorListener = new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
LogUtil.d_3(TAG, " mMediaPlayerErrorListener : error1="+framework_err+" ::: error2="+impl_err);
errorCount++;
if(errorCount>2){
TCLToast.makeText(mContext,"获取视频源失败,请重试!", Toast.LENGTH_LONG).show();
//停止播放视频
myHandler.sendEmptyMessage(STOP_PLAY_VIDEO);
}else {
LogUtil.d_2(TAG,"重新获取视频 : "+errorCount);
myHandler.removeCallbacksAndMessages(null);
myHandler.sendEmptyMessage(STOP_PLAY_VIDEO);
myHandler.sendEmptyMessage(START_PLAY_VIDEO);
}
return true;
}
};
private MediaPlayer.OnVideoSizeChangedListener mSizeChangeListener = new MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
try {
// 获取控件的宽度
int viewWidth = thisView.getWidth();
int viewHeight = thisView.getHeight();
// 在资源尺寸可以播放观看时处理
if (width > 0 && height > 0) {
// 拉伸比例
float scaleWidth = (float) viewWidth / (float) width;
float scaleHeight = (float) viewHeight / (float) height;
if(scaleWidth != scaleHeight) {
float scale = scaleWidth > scaleHeight ? scaleHeight : scaleWidth;
// 设置surfaceview画布大小,即控件的大小
mSurfaceHeight = (int) (scale * height);
mSurfaceWidth = (int) (scale * width);
// 请求调整
requestLayout();
}
}
}catch (Exception ex){
ex.printStackTrace();
}
}
};
}