Exo Player — HMS Video Kit Comparison by building a flavored Android Application . Part VII

Recycler View Approach

In this part, we’ll follow more or less a similar algorithm to instantiate the player

class HmsGmsVideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
    OnPlayWindowListener, OnWisePlayerListener, OnInteract {

    val parent = itemView

    private val frameLayout: FrameLayout
    private val surfaceView: SurfaceView
    private val extender: ImageButton
    private val contentSocialLayout: LinearLayout
    private val contentInfoLayout: LinearLayout
    private val videoText: TextView
    private val contentControlLayout: LinearLayout
    private val r10s: ImageButton
    private val playBtn: ImageButton
    private val f10s: ImageButton
    private val seekBar: SeekBar
    private val lottieAnimationView: LottieAnimationView
    private val infoPanelBtn: ImageButton

     val sLovely:TextView
     val sMessage:TextView
     val sShare:TextView

    private var dialog = Dialog(parent.context, R.style.BlurTheme)

    private lateinit var vServiceProvider: TextView
    private lateinit var vVideoUrl : TextView
    private lateinit var vVideoSender : TextView
    private lateinit var vVideoSenderID : TextView
    private lateinit var vWidthHeight : TextView
    private lateinit var vPlayMode : TextView
    private lateinit var vBitrate : TextView
    private lateinit var vVideoLovely : TextView
    private lateinit var vVideoComments : TextView

    private var isPlaying = false
    private val mStopHandler = false

    private val player: WisePlayer? by lazy {
        WisePlayerInit.createPlayer()
    }

    private val mHandler: Handler by lazy { Handler() }

    private val runnable: Runnable by lazy {
        Runnable {
            configureContentV()
            configureControlV()
            if (!mStopHandler) {
                mHandler.postDelayed(runnable, Constants.DELAY_SECOND)
            }
        }
    }
…
}

Let’s start by filling overridden methods and custom ones. If below part misses any overridden method you may leave them as they are:

  • init()
init {
    //initialize()
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player init")
    frameLayout = parent.findViewById(R.id.frameLayout)
    surfaceView = parent.findViewById(R.id.surfaceView)

    extender = parent.findViewById(R.id.extend_control)
    contentSocialLayout = parent.findViewById(R.id.content_social)
    contentInfoLayout = parent.findViewById(R.id.content_info)
    videoText = parent.findViewById(R.id.video_name)
    contentControlLayout = parent.findViewById(R.id.content_control)
    r10s = parent.findViewById(R.id.r10s)
    playBtn = parent.findViewById(R.id.play_btn)
    f10s = parent.findViewById(R.id.f10s)
    seekBar = parent.findViewById(R.id.seekBar)
    lottieAnimationView = parent.findViewById(R.id.lottieView)
    infoPanelBtn = parent.findViewById(R.id.info_panel_btn)

    sLovely = parent.findViewById(R.id.lovely)
    sMessage = parent.findViewById(R.id.comment)
    sShare = parent.findViewById(R.id.share)

    lottieAnimationView.visibility = View.VISIBLE

    initDialog()
    initUI(R.style.DialogSlide)
}
  • shareUri(…)
fun shareUri(uri:String){
    val sharingIntent = Intent(Intent.ACTION_SEND)
    sharingIntent.type="text/html"
    sharingIntent.putExtra(Intent.EXTRA_SUBJECT,"Share Video - Entertainment")
    sharingIntent.putExtra(Intent.EXTRA_TEXT,uri)
    parent.context.startActivity(Intent.createChooser(sharingIntent,"Share Video"))
}
  • initDialog()
fun initDialog(){
    val width = ViewGroup.LayoutParams.WRAP_CONTENT
    val height = ViewGroup.LayoutParams.WRAP_CONTENT
    dialog.window!!.setLayout(width, height)
    dialog.window!!.attributes.windowAnimations = R.style.DialogSlide
    dialog.setContentView(R.layout.dialog_video_info)
    dialog.setCanceledOnTouchOutside(true)

    vServiceProvider = dialog.findViewById(R.id.d_video_service_provider)
    vVideoUrl = dialog.findViewById(R.id.d_video_url)
    vVideoSender = dialog.findViewById(R.id.d_video_sender)
    vVideoSenderID = dialog.findViewById(R.id.d_video_sender_id)
    vWidthHeight = dialog.findViewById(R.id.d_video_width_height)
    vPlayMode = dialog.findViewById(R.id.d_video_play_mode)
    vBitrate = dialog.findViewById(R.id.d_video_bitrate)
    vVideoLovely = dialog.findViewById(R.id.d_video_given_lovelies)
    vVideoComments = dialog.findViewById(R.id.d_video_total_comments)
}
  • bindInformativeDialog()
fun bindInformativeDialog() {

    val vExit = dialog.findViewById<TextView>(R.id.d_exit)

    vServiceProvider.text = parent.context.getString(R.string.hms_video)
    vWidthHeight.text = parent.context.getString(R.string.video_width_height_params,player?.videoWidth.toString(),player?.videoHeight.toString())
    vPlayMode.text = player?.playMode.toString()
    vBitrate.text = "${player?.currentStreamInfo?.bitrate?.toString()}"

    vExit.setOnClickListener { dialog.dismiss() }
    dialog.show()
}
  • bindDialogInfo(…)
fun bindDialogInfo(vUrl:String, vSender:String, vSenderID:String, vLovely:String){
    vVideoUrl.text=vUrl
    vVideoSender.text=vSender
    vVideoSenderID.text=vSenderID
    vVideoLovely.text = NumberConvertor.prettyCount(vLovely.toLong())
}
  • bindComments(…)
fun bindComments(vComments:String){
    vVideoComments.text = NumberConvertor.prettyCount(vComments.toLong())
}
  • bindVisibility()
override fun bindVisibility() {
    extender.setOnClickListener {
        contentInfoLayout.expandView()
        contentControlLayout.expandView()
        contentSocialLayout.expandView()
        seekBar.expandView()

        if (seekBar.visibility == View.GONE)
            extender.setImageResource(R.drawable.ic_arrow_drop_up)
        else
            extender.setImageResource(R.drawable.ic_arrow_drop_down)
    }
}
  • initUI(…)
override fun initUI(type: Int) {
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player initUI")
    player?.setReadyListener(this)
    player?.setErrorListener(this)
    player?.setEventListener(this)
    player?.setResolutionUpdatedListener(this)
    player?.setLoadingListener(this)
    player?.setPlayEndListener(this)
    player?.setSeekEndListener(this)

    f10s.setOnClickListener {
        player?.seek(player?.currentTime!!+10000)
    }

    r10s.setOnClickListener {
        player?.seek(player?.currentTime!!-10000)
    }

    surfaceView.holder.addCallback(this)

    player?.cycleMode = PlayerConstants.CycleMode.MODE_NORMAL

    playBtn.setOnClickListener {
        changePlayState()
    }

    infoPanelBtn.setOnClickListener {
        bindInformativeDialog()
    }
}
  • configureContentV()
override fun configureContentV() {
    showLogInfo(
        Constants.mHmsGmsVideoViewHolder,
        "Player width: ${player?.videoWidth} - Height: ${player?.videoHeight}"
    )
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player is playing: ${player?.isPlaying}")
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "Player play mode: ${player?.playMode}")
    showLogInfo(
        Constants.mHmsGmsVideoViewHolder, "Player bitrate: ${
            player?.currentStreamInfo?.bitrate?.toString()
        }"
    )
}
  • configureControlV()
override fun configureControlV() {
    seekBar.max = player?.duration!!
    seekBar.progress = player?.currentTime!!
    seekBar.secondaryProgress = player?.bufferTime!!
}
  • restartPlayer(…)
override fun restartPlayer(videoUrl: String) {
    player?.reset()
    player?.setMute(false)
    readyPlayer(videoUrl, videoUrl)
    player?.setView(surfaceView)

    player?.resume(PlayerConstants.ResumeType.KEEP)
}
  • changePlayState()
override fun changePlayState() {
    if (isPlaying) {
        player?.pause()
        isPlaying = false
        setPlayView()
    } else {
        player?.start()
        isPlaying = true
        setPauseView()
    }
}
  • setPauseView()
override fun setPauseView() {
    playBtn.setImageResource(R.drawable.ic_pause)
    lottieAnimationView.visibility=View.GONE
}
  • setPlayView()
override fun setPlayView() {
    playBtn.setImageResource(R.drawable.ic_play)
    lottieAnimationView.visibility=View.VISIBLE
}
  • readyPlayer(…)
override fun readyPlayer(videoUrl: String, string: String) {
    player?.setPlayUrl(videoUrl)
    player?.ready()
    videoText.text = string
}
  • surfaceCreated(…)
override fun surfaceCreated(holder: SurfaceHolder?) {
    player?.setView(surfaceView)
    player?.resume(PlayerConstants.ResumeType.KEEP)
}
  • surfaceChanged(…)
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
    player?.setSurfaceChange()
}
  • surfaceDestroyed(…)
override fun surfaceDestroyed(holder: SurfaceHolder?) {
    player?.suspend()
}
  • onSurfaceTextureAvailable(…)
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
    player?.resume(PlayerConstants.ResumeType.KEEP)
}
  • onReady(…)
override fun onReady(p0: WisePlayer?) {
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "On Ready")
    player?.start()
    isPlaying = true
    val activity = MainActivity()
    activity.runOnUiThread {
        configureControlV()
        mHandler.postDelayed(runnable, Constants.DELAY_SECOND)
    }
}
  • onPlayEnd(…)
override fun onPlayEnd(p0: WisePlayer?) {
    showLogInfo(Constants.mHmsGmsVideoViewHolder, "onPlayEnd")
    isPlaying = false
    setPlayView()
}
  • onStartTrackingTouch(…)
override fun onStartTrackingTouch(seekBar: SeekBar?) {
    seekBar?.progress?.let { player?.seek(it) }
}
  • onStopTrackingTouch(…)
override fun onStopTrackingTouch(seekBar: SeekBar?) {
    seekBar?.progress?.let { player?.seek(it) }
}

Comparing Exo Player with HMS Video Kit

ExoPlayer

  • Easy to use lightweight, easy to implement
  • For audio-only playback on some devices consumes more battery than standard MediaPlayer.
  • Support for DASH and SmoothStreaming, HLS
  • Uses Widevine common encryption.
  • API level of at least 23 required to operate
  • Video stutters when it fills the screen.
  • Initial video loads almost instantly but the speed of loading degrades over the length of the video.
  • Supports playing “m3u8” live stream formats.
  • Reference for Exo Player

HMS Video Kit

  • Initial implementations were sometimes complicate things.
  • For audio-only playback on some devices consumes more battery than standard MediaPlayer.
  • Supports 3GP, MP4, or TS format and compliant with HTTP/HTTPS, HLS, or DASH. Local videos are not supported.
  • Uses WisePlay DRM
  • HMS Core (APK) 4.0.1.300 or later and above is required to operate
  • Video operates normally on a filled screen.
  • Initial video load takes time but video almost never falls back down to loading state over the length of the video.
  • Reference for HMS Video Kit

Output

Main Activity
Record Activity
Profile Activity
Post Comments
Single Player

References

HMS

GMS

Pages: 1 2