文章来自:http://blog.youkuaiyun.com/intbird 转载请说明出处
五一第一天在家休息(后续休息有空会进行升级)
看了一下视频播放的相关东西
写了一个简单的触摸视频播放器
(使用 MediaPlayer 和 ExoPlayer )
我喜欢从自己的使用感受做些调整
所以在里面有一个 触摸平滑进度
的实现
还有就是代码面向抽象,方便后续一些功能替换
有空的话我就会继续写写它
Github源码地址: https://github.com/intbird/VideoPlayerLib
GitHub issues(持续维护,待开发):
https://github.com/intbird/VideoPlayerLib/issues/2
基础功能:
播放不同的 分辨率
/字幕
/倍速
/初始进度
等
在屏幕中间双击: 切换播放
/暂停
在屏幕中间滑动: 拖动进度
在屏幕左侧滑动: 控制亮度
在屏幕右侧滑动: 控制声音
点击锁定按钮: 锁定
当前所有操作
点击上一个
/下一个
/播放
/暂定
/停止
: 执行对应动作
横竖屏方向自动切换
有时间的话会继续开发......
完成效果:
- 部分截图:
Github源码地址: https://github.com/intbird/VideoPlayerLib
文章来自:http://blog.youkuaiyun.com/intbird 转载请说明出处
结构解析:
0.权限检查: 读取媒体权限
1.播放器层: 播放器的接口 + 实现
2.触控层: 触摸区域识别 + 手势识别 + 触摸灵敏度和进度反馈
3.控制面板层: 可视UI按钮(上一个,下一个,播放/暂停/停止)
4.锁定层: 锁(锁播放器+ 锁触控 + 锁面板+ 锁屏幕方向等)
代码结构:
用mvc简单实现一下,有空了可以把view这层在做层封装,方便后续更换UI的最小代价
1.项目结构
1.api: 使用Fragment(推荐此方式)
VideoPlayerFragment.newInstance(array, index, style, autoPlay)
2.api: 使用Activity(模块解耦实现)
ServicesLoader.load(IVideoPlayer::class.java)?.startActivity(this,array, index, autoPlay)
显示声明外部api和实现默认的api
3.总体结构
逐步升级, 代码结构有些许调整
4.资源前缀
5.播放实现
- MediaPlayerImpl
intbird.soft.lib.video.player.main.player.player.MediaPlayerImpl
- ExoPlayerImpl(有空会继续实现)
intbird.soft.lib.video.player.main.player.player.ExoPlayerImpl
6.自动旋屏
自动旋转视频和抖动恢复屏幕方向 : https://blog.youkuaiyun.com/intbird/article/details/109077762
2. 大体思路
1. 不同播放器不同布局, 统一接口多个回调
1.面向接口, 抽象代码
private var playerCall: PlayerCallbacks? = null
private var playerView: MediaViewInfo<out View, out View>? = null
private var player: IPlayer? = null
private fun instanceMediaPlayer(mediaPlayerType: MediaPlayerType?) {
when(mediaPlayerType) {
MediaPlayerType.PLAYER_STYLE_1, MediaPlayerType.PLAYER_STYLE_2 -> {
playerCall = PlayerCallbacks(playerCallback, videoPlayerCallback)
playerView = MediaViewProvider(view).by(mediaPlayerType)
player = MediaPlayerImpl(getInternalActivity(), playerView?.display as? TextureView, subtitleText, intentHelper, playerCall)
}
MediaPlayerType.PLAYER_STYLE_3 -> {
playerCall = PlayerCallbacks(playerCallback, videoPlayerCallback)
playerView = MediaViewProvider(view).by(mediaPlayerType)
player = ExoPlayerImpl(getInternalActivity(), playerView?.display as? TextureView, playerCall)
}
}
}
2.面向复杂, 抽象配置
通过一个UI的配置项,强制让不同类型的播放自己选择如何和界面关联
enum class MediaPlayerType(
open val layoutDisplay: Int,
open val viewDisplay: Int,
open val layoutControl: Int,
open val viewControl: Int
) {
PLAYER_STYLE_1(
R.layout.lib_media_player_diaplay_texture,
R.id.textureView,
R.layout.lib_media_player_control_style_1,
R.id.mediaRootControl
),
PLAYER_STYLE_2(
R.layout.lib_media_player_diaplay_texture,
R.id.textureView,
R.layout.lib_media_player_control_style_2,
R.id.mediaRootControl
),
PLAYER_STYLE_3(
R.layout.lib_media_player_diaplay_ijkplayer
R.id.ijkplayer,
R.layout.lib_media_player_control_style_2,
R.id.mediaRootControl
)
}
2. 动态修改播放参数,回传到播放器/界面等外部
1. IntentHelper
IntentHelper: 获取外部传入的Intent列表 的 某个Index
进而进行组装当前播放的数据
但:
我想如果一个player实现类,他的数据不是外部传入的
那么:
他也可以替换掉这个IntentHelper,在少量修改后要
能将 切换播放/上一首/下一首 等改为自己的内部方法
始终根据index获取数据
2. 实现代理方法
3. 参数更新
MediaItemInfoRecord: 通过它来保存播放状态,
以便在切换分辨率,字幕等需要强行进行状态改变后
继续操作前的进度进行播放
onReceivePlayFile: 这个在收到播放参数变更时会通知到播放器/控制层等等