记录VideoPlayer播放器封装的从0到1

13 阅读2分钟

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")