androidx / androidx/media

RTSP: Support LoadErrorHandlingPolicy

Open
#435 2 comments 2 reactions 1 assignee View on GitHub

@microkatz is already working on this.

Since Jun 2, 2023.

enhancement needs triage
Dominant language
Java
Stars
3k
Forks
955
Avg merge
12d 14h
Merged PRs (30d)
2

Description

Media3 Version

Media3 1.0.2

Devices that reproduce the issue

Custom Android device which is used to control a drone

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Yes

Reproduction steps

I am using the following code to stream RTSP video from a webcam to our custom Android device:

private const val TAG = "PlayerActivity"

 /**
 * A fullscreen activity to play audio or video streams.
 */
class PlayerActivity : AppCompatActivity() {

    private val playbackStateListener: Player.Listener = playbackStateListener()

     @UnstableApi
    private val onLiveStreamEndedListener = onLiveStreamEndedListener()

    private var player: ExoPlayer? = null

    private fun playbackStateListener() = object : Player.Listener {

        override fun onPlaybackStateChanged(playbackState: Int) {
            var stateString = "UNKNOWN_STATE             -"
            when (playbackState) {
                ExoPlayer.STATE_IDLE -> stateString = "ExoPlayer.STATE_IDLE      -"
                ExoPlayer.STATE_BUFFERING -> stateString = "ExoPlayer.STATE_BUFFERING -"
                ExoPlayer.STATE_READY -> stateString = "ExoPlayer.STATE_READY     -"
                ExoPlayer.STATE_ENDED -> {
                    stateString = "ExoPlayer.STATE_ENDED     -"
                }
            }
            Log.d(TAG, "changed state to $stateString")
        }
    }

    @OptIn(UnstableApi::class)
    private fun onLiveStreamEndedListener() = object : DefaultLoadErrorHandlingPolicy() {

        override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
            return 1000
        }

        override fun getMinimumLoadableRetryCount(dataType: Int): Int {
            return Int.MAX_VALUE
        }
    }

    private val viewBinding by lazy(LazyThreadSafetyMode.NONE) {
        ActivityPlayerBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(viewBinding.root)
    }

    public override fun onStart() {
        super.onStart()
        initializePlayer()
    }

    public override fun onResume() {
        super.onResume()
        hideSystemUi()
        if (player == null) {
            initializePlayer()
        }
    }


    public override fun onStop() {
        super.onStop()
        releasePlayer()
    }

    @OptIn(UnstableApi::class)
    private fun initializePlayer() {
        val mediaSourceFactory = DefaultMediaSourceFactory(this)
            .setLoadErrorHandlingPolicy(onLiveStreamEndedListener)

        player = ExoPlayer.Builder(this)
            .setMediaSourceFactory(mediaSourceFactory)
            .build()
            .also { exoPlayer ->
                viewBinding.videoView.player = exoPlayer
                viewBinding.videoView.useController = false
                val mediaItem = MediaItem.Builder()
                    .setUri("rtsp://192.168.0.10:8554/H264Video")
                    .setMimeType(MimeTypes.APPLICATION_RTSP)
                    .setLiveConfiguration(MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build())
                    .build()
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.playWhenReady = true
                exoPlayer.addListener(playbackStateListener)
                exoPlayer.prepare()
            }
    }

    private fun releasePlayer() {
        player?.let { exoPlayer ->
            exoPlayer.removeListener(playbackStateListener)
            exoPlayer.release()
        }
        player = null
    }

    @SuppressLint("InlinedApi")
    private fun hideSystemUi() {
        WindowCompat.setDecorFitsSystemWindows(window, false)
        WindowInsetsControllerCompat(window, viewBinding.videoView).let { controller ->
            controller.hide(WindowInsetsCompat.Type.systemBars())
            controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        }
    }
}

The live stream works when the camera is connected. If I disconnect the camera, the live stream stops as it should. However, when I reconnect the camera, the live stream doesn't resume even though I have declared a custom policy using the variable onLiveStreamEndedListener and set it for the exoplayer using the MediaSourceFactory.

Expected result

Livestream video should resume after the camera is reconnected

Actual result

Livestream video does not resume even after the camera is reconnected

Media

It is livestream from a custom camera.

Bug Report
  • You will email the zip file produced by adb bugreport to dev.exoplayer@gmail.com after filing this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.