VideoPlayer封装 (点播篇)
思考1:业务中需要一个播放器工具类(播放控制器)PlayerHelper。该工具类为业务层提供各种行为能力(绑定控件、数据准备、播放、停止、暂停、恢复、释放资源、切换资源等)和状态监听(空闲中、准备中、播放中、暂停、结束、错误)。同时工具类持有播放器对象和播放控件,解耦了UI层与播放器实例。
思考2:播放器可能有很多选择,例如腾讯播放器、阿里播放器、media3播放器等等,不同厂家SDK提供的API、能力和状态回调都不一样,所以我们定义播放器接口IPlayer,来规范播放器应具备哪些能力。工具类持有播放器接口的具体实现,具体是腾讯的还是阿里的,不重要,反正玩的都是IPlayer的能力(多态),后续更换其它播放器实现IPlayer就行,炒鸡简单~
定义播放器状态 (持续的状态)
enum class PlayerState {
IDLE,
PREPARING,
PLAYING,
PAUSING,
END,
ERROR,
}
定义播放器接口IPlayer (行为能力)
interface IPlayer {
fun surface(surface: View)
fun start (url:String)
fun stop ()
fun pause ()
fun resume ()
fun release ()
fun stateListener(listener:StateListener)
}
interface StateListener {
fun onStateChange(state: PlayerState)
}
播放器实现拿Media3举个栗子
class IPlayerImplMedia3(context: Context) : IPlayer {
var player: ExoPlayer? = null
var endTag = false
init {
player = ExoPlayer.Builder(context).build()
player?.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_IDLE -> {
listener?.onStateChange(PlayerState.IDLE)
}
Player.STATE_BUFFERING -> {
listener?.onStateChange(PlayerState.PREPARING)
}
Player.STATE_READY -> {
player?.play()
}
Player.STATE_ENDED -> {
endTag = true
}
}
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
if(isPlaying){
listener?.onStateChange(PlayerState.PLAYING)
}else{
if(!endTag){
listener?.onStateChange(PlayerState.PAUSING)
}else{
endTag = false
listener?.onStateChange(PlayerState.END)
}
}
}
override fun onIsLoadingChanged(isLoading: Boolean) {
}
override fun onPlayerError(error: PlaybackException) {
listener?.onStateChange(PlayerState.ERROR)
}
})
}
override fun surface(surface: View) {
when (surface) {
is SurfaceView -> {
player?.setVideoSurfaceView(surface)
}
is TextureView -> {
player?.setVideoTextureView(surface)
}
is PlayerView -> {
surface.player = player
}
}
}
override fun start(url: String) {
player?.setMediaItem(MediaItem.fromUri(url))
player?.prepare()
player?.playWhenReady = true
}
override fun pause() {
player?.pause()
}
override fun resume() {
player?.play()
}
override fun stop() {
player?.stop()
}
override fun release() {
player?.release()
}
var listener: StateListener? = null
override fun stateListener(listener: StateListener) {
this.listener = listener
}
}
主菜PlayerHelper工具类来了
class PlayerHelper(var iPlayer: IPlayer,lifecycle: Lifecycle):DefaultLifecycleObserver {
init {
lifecycle.addObserver(this@PlayerHelper)
}
fun surface(surface: View) {
iPlayer.surface(surface)
}
fun start(url: String) {
iPlayer.start( url)
}
fun pause() {
iPlayer.pause()
}
fun resume(){
iPlayer.resume()
}
fun stop() {
iPlayer.stop()
}
fun release() {
iPlayer.release()
}
fun setStateListener(listener: StateListener) {
iPlayer.stateListener(listener)
}
/*生命周期*/
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
stop()
release()
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
pause()
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
resume()
}
}
构造参数1:播放器接口(多态,播放器实例传递进来) 构造参数2:生命周期控制(便于管理播放器的暂停、恢复、释放等)
业务层调用
//1.PlayerHelper-create
var playerHelper = PlayerHelper(IPlayerImplMedia3(this@MainActivity), lifecycle).apply {
//2.PlayerHelper-bindView
surface(viewBinding.mPlayerView)
//3.PlayerHelper-stateListener
setStateListener(object : StateListener {
override fun onStateChange(state: PlayerState) {
when {
state == PlayerState.IDLE -> {
Log.e("ISHIBASHI","it is IDLE")
}
state == PlayerState.PREPARING -> {
Log.e("ISHIBASHI","it is PREPARING")
}
state == PlayerState.PLAYING -> {
Log.e("ISHIBASHI","it is PLAYING")
}
state == PlayerState.PAUSING -> {
Log.e("ISHIBASHI","it is PAUSING")
}
state == PlayerState.END -> {
Log.e("ISHIBASHI","it is END")
}
state == PlayerState.ERROR -> {
Log.e("ISHIBASHI","it is ERROR")
}
}
}
})
}
//4.PlayerHelper-start
playerHelper.start("https://vjs.zencdn.net/v/oceans.mp4")