Search code examples
androidexoplayersoundpool

How to play Video & Audio in the same time, asynchronously in Android


I am trying to implement the following, for a workout android app:

  1. A countdown timer is set and counts down time (lets say from 30sec to 0).
  2. From the moment the timer starts, a repeatable exercise mute video starts playing in a loop, until the timer reaches to 0, where the next exercise video comes, and the timer is reset to 30 counting down again.
  3. While the timer is running, audio is played simultaneously with the video to indicate to the user various things (ie. "Begin Workout", "3-2-1", "Next Exercise" etc.)

My question is, how do you combine video with Audio? For Video I use ExoPlayer. Is it good to combine ExoPlayer with SoundPool for Audio? Should I create multiple instances of ExoPlayer for both (and if so, to what do I bind the AudioPlayer Instance)?


Solution

  • According to this I found that the best solution was to use ExoPlayer for videos, and SoundPool for short audio speech indications.

    I use an observer function in which I listen for timer events (ie tick == 4000) and use soundPool.play() each time the event happens.

    For the Video, I listen to Timer Events (PLAYING, PAUSED, STOPPED) and use videoPlayer.play() as well.

    See below code as an example:

        private fun subscribeObservers() {
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
    
                launch {
                    sharedViewModel.timer.tick.collect { tick ->
                        // tick can only be multiple of 1000
                        if (tick == 0L) {
                            binding.startWorkoutTvTimer.text = ""
                        } else {
                            binding.startWorkoutTvTimer.text = (tick / 1000).toString()
                        }
    
                        if (tick == 4000L) {
                            sharedViewModel.soundPool?.play(sharedViewModel.countdownSoundId, 1F, 1F, 0, 0, 1F)
                        }
    
                        // SoundPool needs to be preloaded from previous Fragment
                        if (tick == 10000L) {
                            if (sharedViewModel.currentExercise == 0) {
                                sharedViewModel.soundPool?.play(sharedViewModel.beginWorkoutSoundId, 1F, 1F, 1, 0, 1F)
    
                            } else {
                                sharedViewModel.soundPool?.play(sharedViewModel.goSoundId, 1F, 1F, 1, 0, 1F)
                            }
    
                        }
                    }
                }
    
    
                launch {
                    sharedViewModel.timer.timerMode.collect { playerMode ->
                        when (playerMode) {
                            TimerMode.PLAYING -> {
                                binding.startWorkoutBtnPause.setBackgroundResource(R.drawable.ic_baseline_pause_24)
                                videoPlayer?.play()
                            }
    
                            TimerMode.PAUSED -> {
                                binding.startWorkoutBtnPause.setBackgroundResource(R.drawable.ic_baseline_play_arrow_24)
                                videoPlayer?.pause()
                            }
    
                            TimerMode.STOPPED -> {
                            }
                        }
                    }
                }
    
    
            }
    
        }
    }