这是indexloc提供的服务,不要输入任何密码
Skip to content

onPlayerError not called when LoadErrorHandlingPolicy.getRetryDelayMsFor() returns C.TIME_UNSET #2401

@bagsu78

Description

@bagsu78

Version

Media3 1.1.1 / ExoPlayer 2.19.1

More version details

use ExoPlayer 2.19.1

Devices that reproduce the issue

Samsung S22 running Android 14
Samsung Z Filp5 running Android 14

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Not tested

Reproduction steps

Reproduction Steps

  1. Start playback of a streaming audio source (e.g. a live radio stream).
  2. Throttle the network to simulate poor connectivity:
    • Go to Settings > Developer options
    • Under Networking, set “Network download speed” to 128 kbps.
  3. Observe playback:
    • During playback, the player intermittently enters an infinite buffering state.
  4. Restore network to a good connection:
    • Even with normal network speeds (e.g. exiting the tunnel or disabling throttling), the player remains stuck buffering.

Expected result

Playback should stop and onPlayerError(…) should fire when LoadErrorHandlingPolicy.getRetryDelayMsFor() returns C.TIME_UNSET.
The following code is the LoadErrorHandlingPolicy I applied.

internal class XXXLoadErrorHandlingPolicy : DefaultLoadErrorHandlingPolicy() {

    override fun getFallbackSelectionFor(
        fallbackOptions: LoadErrorHandlingPolicy.FallbackOptions,
        loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo
    ): LoadErrorHandlingPolicy.FallbackSelection? {
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - fallbackOptions.numberOfExcludedTracks: ${fallbackOptions.numberOfExcludedTracks}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - fallbackOptions.numberOfLocations: ${fallbackOptions.numberOfLocations}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - fallbackOptions.numberOfTracks: ${fallbackOptions.numberOfTracks}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - fallbackOptions.numberOfExcludedLocations: ${fallbackOptions.numberOfExcludedLocations}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - loadErrorInfo.exception: ${loadErrorInfo.exception}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - loadErrorInfo.loadEventInfo.uri: ${loadErrorInfo.loadEventInfo.uri}")
        Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - loadErrorInfo.mediaLoadData.dataType: ${loadErrorInfo.mediaLoadData.dataType}")
        return super.getFallbackSelectionFor(fallbackOptions, loadErrorInfo).also {
            Log.d("LoadErrorHandlingPolicy", "getFallbackSelectionFor - return: $it")
        }
    }

    override fun isEligibleForFallback(exception: IOException): Boolean {
        Log.d("LoadErrorHandlingPolicy", "isEligibleForFallback - exception: $exception")
        return super.isEligibleForFallback(exception)
    }

    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
        val exception = loadErrorInfo.exception

        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - exception: $exception")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - exception.cause: ${exception.cause}")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - exception.message: ${exception.message}")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - exception.localizedMessage: ${exception.localizedMessage}")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - loadErrorInfo.errorCount: ${loadErrorInfo.errorCount}")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - loadErrorInfo.mediaLoadData.dataType: ${loadErrorInfo.mediaLoadData.dataType}")
        Log.d("LoadErrorHandlingPolicy", "getRetryDelayMsFor - loadErrorInfo.loadEventInfo.uri: ${loadErrorInfo.loadEventInfo.uri}")


        return C.TIME_UNSET

    }

    override fun getMinimumLoadableRetryCount(dataType: Int): Int {
        Log.d("LoadErrorHandlingPolicy", "getMinimumLoadableRetryCount - dataType: $dataType")
        return super.getMinimumLoadableRetryCount(dataType)
    }
}

Below is the log I encountered when this issue occurred (I’ve also included logs from the PlayerEventsListener’s onEvents function).
After the log below, no further output is printed and playback remains stuck buffering (even after restoring the network to normal).

Image

Actual result

I expected onPlayerError() to be invoked after getRetryDelayMsFor() returned C.TIME_UNSET, and intended to perform the retry logic inside that callback. However, despite returning C.TIME_UNSET, the onPlayerError() callback was never called.

When playing a streaming audio source, I use three URIs in sequence—playlist.m3u8, chunklist.m3u8, and the .aac segment URLs. However, if a load error occurs specifically on the chunklist.m3u8 URI under poor network conditions, onPlayerError() is never invoked. Through extensive testing, I’ve confirmed that this only happens when the network is throttled; under a good connection, even HTTP errors like 404 or 500 correctly trigger onPlayerError().

Media

the media is confidential and cannot be shared publicly. sry

Bug Report

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions