
Android 实现视屏播放器与边播边缓存功能外加蹲坑铲屎(IJKPlayer)
效果

Android上最为人熟悉的MediaPlayer,对,就是这货,在上两篇音频文章中频频露脸的家伙,这次又有它的身影,然而还是上次不讲他,就连他的封装类VideoView也不讲<( ̄︶ ̄)>,呸呸呸,又扯了一堆没用的。
集成工作而是有定的工作量的,它的DEMO肯定满足不了欲求不满的设计狮和产品汪的,这里我们不跑分,不打广告,不讲原理,只求站在巨人的肩膀上学(cao)习(xi),快速集成。
单例,没得商量,它应该负责真正的播放请求与显示逻辑,集成了IjkMediaPlayer,BILIBLI的开源小组还是很有心的,它的封装和接口使用基本和MediaPlayer没有什么差别,只还要用出来就好了。‘
这里我们要实现IjkMediaPlayer的播放接口,IjkMediaPlayer的相关状况回调之后封发到各个逻辑播放器中。从下方代码可以看见,真的和MediaPlayer好像。
mediaPlayer = new IjkMediaPlayer();
//音频类型
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//数据源
mediaPlayer.setDataSource(((GSYModel) msg.obj).getUrl(), ((GSYModel) msg.obj).getMapHeadData());
//播放完成
mediaPlayer.setOnCompletionListener(GSYVideoManager.this);
//缓冲
mediaPlayer.setOnBufferingUpdateListener(GSYVideoManager.this);
//常亮
mediaPlayer.setScreenOnWhilePlaying(true);
//加载完毕
mediaPlayer.setOnPreparedListener(GSYVideoManager.this);
//拖动
mediaPlayer.setOnSeekCompleteListener(GSYVideoManager.this);
//失败
mediaPlayer.setOnErrorListener(GSYVideoManager.this);
//视频相关信息-重要
mediaPlayer.setOnInfoListener(GSYVideoManager.this);
//视频大小
mediaPlayer.setOnVideoSizeChangedListener(GSYVideoManager.this);】
//开始加载
mediaPlayer.prepareAsync();
的回调接口里,大部分大家都耳目能详吧,没听过也没关系,都写上就对了,但是最主要需要关注的两个,一个是通过setOnVideoSizeChangedListener拿到视频宽和高,这是我们后续正常显示视频的借助之一。

另外一个就是setOnInfoListener,这里我们主要是获得到视频相关的元信息里视频旋转角度!还记得那时候对视频播放不熟悉,和产品也有QA力争“这个视频其实就是转了90度的,我就不改,你咬我吗···”这样的黑历史。Σ( ° △ °|||)
特别是Android拍摄的竖屏视频,旋转不是视频本来的图像,而是提高了旋转信息,而这个之后你必须做的就是识别它,然后转了它丫的。另外,因为Android本身的MediaPlaer和VideoView自身就处理好可是不需要你旋转。((ノO益O)ノ彡┻━┻亲生的啊)
这里的接口主要是把当前播放的视频状况和信息到返回到逻辑播放器中。
@Override
public void onInfo(int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
BACKUP_PLAYING_BUFFERING_STATE = mCurrentState;
setStateAndUi(CURRENT_STATE_PLAYING_BUFFERING_START);
} else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
if (BACKUP_PLAYING_BUFFERING_STATE != -1) {
setStateAndUi(BACKUP_PLAYING_BUFFERING_STATE);
BACKUP_PLAYING_BUFFERING_STATE = -1;
}
} else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED) {
//这里返回了视频旋转的角度,根据角度旋转视频到正确的画面
mRotate = extra;
if (mTextureView != null)
mTextureView.setRotation(mRotate);
}
}
为什么不用SurfaceView?因为TextureView很可爱啊。这里我们主要对于视频的大小和旋转视角设定TextureView的大小,详细就不多说了(不是懒),挑其中一类讲讲,因为主要只是这个。
觉得看起来有点绕口?没关系,用着用着就习惯了····
width = widthSpecSize;
height = heightSpecSize;
···
if (videoWidth * height < width * videoHeight) {
width = height * videoWidth / videoHeight;
} else if (videoWidth * height > width * videoHeight) {
height = width * videoHeight / videoWidth;
}
···
if (getRotation() != 0 && getRotation() % 90 == 0) {
if (widthS < heightS) {
if (width > height) {
width = (int) (width * (float) widthS / height);
height = widthS;
} else {
height = (int) (height * (float) width / widthS);
width = widthS;
}
} else {
if (width > height) {
height = (int) (height * (float) width / widthS);
width = widthS;
} else {
width = (int) (width * (float) widthS / height);
height = widthS;
}
}
}
所有的UI逻辑基本都可以提到这里,目前继承了 FrameLayout,View.OnClickListener, View.OnTouchListener, SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener和GSYMediaPlayerListener。
逻辑播放器实现的内容很多了,这里主要说几个地方,好吧,我承认我懒╮(╯_╰)╭ ,但是写很多了也没人看啊,所以此处主要是说一些关键的点,有必须留言再开个坑聊一聊,反正有DEMO。

在逻辑播放器中统一分发各类状况,把被播放的manager状态同步到此处,之后你想要在那个逻辑播放器里播放只应该对应的设定状况后把manager的同步过来。
switch (mCurrentState) {
//正常初始化状态
case CURRENT_STATE_NORMAL:
if (isCurrentMediaListener()) {
cancelProgressTimer();
GSYVideoManager.instance().releaseMediaPlayer();
}
break;
//loading中
case CURRENT_STATE_PREPAREING:
resetProgressAndTime();
break;
//播放中
case CURRENT_STATE_PLAYING:
startProgressTimer();
break;
//暂停
case CURRENT_STATE_PAUSE:
startProgressTimer();
break;
//错误-需要判断是否切换了逻辑播放器
case CURRENT_STATE_ERROR:
if (isCurrentMediaListener()) {
GSYVideoManager.instance().releaseMediaPlayer();
}
break;
//结束
case CURRENT_STATE_AUTO_COMPLETE:
cancelProgressTimer();
mProgressBar.setProgress(100);
mCurrentTimeTextView.setText(mTotalTimeTextView.getText());
break;
}
···
case MotionEvent.ACTION_MOVE:
float deltaX = x - mDownX;
float deltaY = y - mDownY;
float absDeltaX = Math.abs(deltaX);
float absDeltaY = Math.abs(deltaY);
//是全屏还是设置了可以触摸
if (mIfCurrentIsFullscreen || mIsTouchWiget) {
//之前是否已经符合了触摸逻辑条件
if (!mChangePosition && !mChangeVolume && !mBrightness) {
//如果手指动了超过一定距离就可以判断是滑动,防止点击的误判的
if (absDeltaX > mThreshold || absDeltaY > mThreshold) {
cancelProgressTimer();
//如果是左右的就是进度
if (absDeltaX >= mThreshold) {
mChangePosition = true;
mDownPosition = getCurrentPositionWhenPlaying();
if (mVideoAllCallBack != null && isCurrentMediaListener()) {
mVideoAllCallBack.onTouchScreenSeekPosition(mUrl, mObjects);
}
} else {
//如果是上下的判断是左边还是右边
if (mFirstTouch) {
mBrightness = mDownX < mScreenWidth * 0.5f;
mFirstTouch = false;
}
if (!mBrightness) {
mChangeVolume = true;
mGestureDownVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
if (mVideoAllCallBack != null && isCurrentMediaListener()) {
mVideoAllCallBack.onTouchScreenSeekVolume(mUrl, mObjects);
}
}
}
}
}
}
···
//根据flag执行逻辑
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-121653-1.html
打不过
加你妹丫