Android VideoView jumps back in time

Viewed 87

I have implemented videoView with custom seek bar. When changing seekbar position the video is playing correctly but the time display is wrong. It happens because the videoView position goes back for some reason. It doesn not happens always but very often.

SeekBar change:

binding.progressBarVideo.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {


            if (underSeek) {
                binding.textViewTime.text = durationToFormat(binding.videoViewF.currentPosition)
                binding.videoViewF.seekTo(seekBar.progress)

                RLog.d("------------------")
                RLog.d("Seekbar progress: ${seekBar.progress}")
                RLog.d("videoView position: ${binding.videoViewF.currentPosition}")
                RLog.d("Time display string: ${durationToFormat(binding.videoViewF.currentPosition)}")
            }
        }

        override fun onStartTrackingTouch(seekBar: SeekBar?) {
            mainHandler.removeCallbacks(updateTextTask)
            underSeek = true
        }

        override fun onStopTrackingTouch(seekBar: SeekBar?) {
            mainHandler.post(updateTextTask)
            underSeek = false
            hideTimer = 0
        }

    })

updateTextTask:

private val updateTextTask = object : Runnable {
    override fun run() {

        RLog.d("----------------------------")
        RLog.d("videoView position: ${binding.videoViewF.currentPosition}")
        RLog.d("Time display string: ${durationToFormat(binding.videoViewF.currentPosition)}")

        binding.textViewTime.text = durationToFormat(binding.videoViewF.currentPosition)
        binding.progressBarVideo.progress = binding.videoViewF.currentPosition
        mainHandler.postDelayed(this, 1000)
                }
}

Duration to Format:

@SuppressLint("DefaultLocale")
private fun durationToFormat(duration: Int): String {

    return java.lang.String.format(
        "%02d:%02d",
        TimeUnit.MILLISECONDS.toMinutes(duration.toLong()),
        TimeUnit.MILLISECONDS.toSeconds(duration.toLong()) -
                TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration.toLong()))
    )
}

Logs:

#onProgressChanged: ------------------
#onProgressChanged: Seekbar progress: 257875
#onProgressChanged: videoView position: 257875
#onProgressChanged: Time display string: 04:17
#run: ----------------------------
#run: videoView position: 257875
#run: Time display string: 04:17
#run: ----------------------------
#run: videoView position: 256957
#run: Time display string: 04:16
#run: ----------------------------

At you can see, after seek the time is 257875, the first timer iteration is also 257875. Then it jumps to 256957 and I dont get why...

2 Answers

i'm not sure, but i guess the problem is with postDelayed, since it actually run after 1 sec. So try to add 1000 ms here, hope it might behave correctly.

RLog.d("Time display string: ${durationToFormat(binding.videoViewF.currentPosition + 1000)}")

It seems that you updating progress again from your runnable, so onProgressChanged will be called from the loop and can reset progress to previous position.

In this case you can filter out progress update from code, and seek to new video position only when fromUser is true at onProgressChanged:

override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
    if (fromUser) {
        binding.videoViewF.seekTo(seekBar.progress)
    }
    binding.textViewTime.text = durationToFormat(binding.videoViewF.currentPosition)
}

private val updateTextTask = object : Runnable {
    override fun run() {
        binding.progressBarVideo.progress = binding.videoViewF.currentPosition
        mainHandler.postDelayed(this, 1000)
    }
}

Also in this case you can remove text update from runnable, because it will be triggered by the ProgressBar position update.

Related