2021-06-02

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();
			}
		}
	};
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缺锌补钙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值