Skip to content

IllegalArgumentException in SampleQueue.discardUpstreamSampleMetadata if download canceled #3143

@AlexICCC

Description

@AlexICCC

Version

Media3 1.8.0

More version details

No response

Devices that reproduce the issue

Devices from crashlytics:

  • Galaxy A26 Android 16
  • Oppo RMX3834 Android 14
  • Samsung Galaxy S21 Android 15
  • Galaxy A54 5G Android 14
  • Galaxy M11 Android 12
  • Redmi Note 12 Pro Android 13
  • Redmi A5 Android 15
  • Realme 14 Pro 5G Android 16

and many others

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Not tested

Reproduction steps

Hello!

I've overrided 'ExoTrackSelection.shouldCancelChunkLoad' to cancel chunks when they are loading for too long. During tests of this feature I've found crashes in crashlytics with stacktrace:

androidx.media3.common.util.Assertions.checkArgument (Assertions.java:40)
androidx.media3.exoplayer.source.SampleQueue.discardUpstreamSampleMetadata (SampleQueue.java:894)
androidx.media3.exoplayer.source.SampleQueue.discardUpstreamSamples (SampleQueue.java:267)
androidx.media3.exoplayer.source.chunk.ChunkSampleStream.discardUpstreamMediaChunksFromIndex (ChunkSampleStream.java:892)
androidx.media3.exoplayer.source.chunk.ChunkSampleStream.onLoadCanceled (ChunkSampleStream.java:514)
androidx.media3.exoplayer.source.chunk.ChunkSampleStream.onLoadCanceled (ChunkSampleStream.java:59)
androidx.media3.exoplayer.upstream.Loader$LoadTask.handleMessage (Loader.java:513)
android.os.Handler.dispatchMessage (Handler.java:115)
android.os.Looper.loopOnce (Looper.java:298)
android.os.Looper.loop (Looper.java:408)
android.os.HandlerThread.run (HandlerThread.java:85)

I checked behaviour of devices which failed and found that many of them (all of them which logs I've read - about 10-20 devices) made multiple 10-seconds "seek" actions right before crash.

Looks like cancel and seek can make discard in the same time and they can conflict between each other.

I have 2 assumptions, why '0 <= discardCount && discardCount <= (length - readPosition)' can return 'false':

  1. discardUpstreamSamples doesn't have 'synchronized' modifier. And it tries to execute in one time with 'commitSample()' (or other call) from another Thread.

  2. Maybe seekTo() or reset() calls interleave with discardUpstreamSampleMetadata(). One of logs which I collected during tries to reproduce.

seekTo() position=469
discardSamples() 468 samples thread=ov-playback-thread
seekTo() position=470
discardSamples() 469 samples thread=ov-playback-thread
seekTo() position=470
discardSamples() 469 samples thread=ov-playback-thread
seekTo() position=470
discardSamples() 469 samples thread=ov-playback-thread
reset()
reset()
reset()
reset()
cancel!
discardUpstreamMediaChunksFromIndex() for primary
discardUpstreamSampleMetadata() discardCount=192 length=716 readPosition=4 thread=ov-playback-thread

Expected result

App works without crash

Actual result

App crashes

Media

n/a

Bug Report

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions