google / ExoPlayer

An extensible media player for Android
Apache License 2.0
21.65k stars 6k forks source link

Getting ExoPlaybackException->IllegalArgumentException during setVideoSurface(null) #6355

Open stari4ek opened 4 years ago

stari4ek commented 4 years ago

[REQUIRED] Issue description

Getting error while releasing surface:

main|E] EventLogger: playerFailed [4.80, 1.08, window=0, period=0]
com.google.android.exoplayer2.ExoPlaybackException: java.lang.IllegalArgumentException 
    at com.google.android.exoplayer2.t.handleMessage(ExoPlayerImplInternal.java:24) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.os.HandlerThread.run(HandlerThread.java:65) 
Caused by: java.lang.IllegalArgumentException: null 
    at android.media.MediaCodec.native_setSurface(Native Method) 
    at android.media.MediaCodec.setOutputSurface(MediaCodec.java:1979) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:176) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:73) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:57) 
    at com.google.android.exoplayer2.t.c(ExoPlayerImplInternal.java:9) 
    at com.google.android.exoplayer2.t.e(ExoPlayerImplInternal.java:4) 
    at com.google.android.exoplayer2.t.d(ExoPlayerImplInternal.java:50) 
    at com.google.android.exoplayer2.t.handleMessage(ExoPlayerImplInternal.java:4) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:193) 
    at android.os.HandlerThread.run(HandlerThread.java:65) 

or

java.lang.IllegalStateException 
    at com.google.android.exoplayer2.t.handleMessage(ExoPlayerImplInternal.java:24) 
    at android.os.Handler.dispatchMessage(Handler.java:101) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.os.HandlerThread.run(HandlerThread.java:65)
Caused by: java.lang.IllegalStateException: null 
    at android.media.MediaCodec.native_setSurface(Native Method) 
    at android.media.MediaCodec.setOutputSurface(MediaCodec.java:1954) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:176) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:73) 
    at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.a(MediaCodecVideoRenderer.java:57) 
    at com.google.android.exoplayer2.t.c(ExoPlayerImplInternal.java:9) 
    at com.google.android.exoplayer2.t.e(ExoPlayerImplInternal.java:4) 
    at com.google.android.exoplayer2.t.d(ExoPlayerImplInternal.java:50) 
    at com.google.android.exoplayer2.t.handleMessage(ExoPlayerImplInternal.java:4) 
    at android.os.Handler.dispatchMessage(Handler.java:101) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.os.HandlerThread.run(HandlerThread.java:65)

I'm not sure if it should be handled by the app, cause of integration specifics, or it can be workarounded with MediaCodeVideoRenderer.codecNeedsSetOutputSurfaceWorkaround. In other cases decoder errors are wrapped to MediaCodecVideoRenderer.VideoDecoderException and it's easier to identify them and apply app-specific handling.

[REQUIRED] Reproduction steps

This is part of "tv input service" which adopts TIF on Android TV. When TvInputService.Session.onSetSurface(null) is called - this call routed to SimpleExoPlayer.setVideoSurface. Due TIF integration specifics - surface is owned by host app (Live TV/Live Channels) and "tv input service" does not own it's lifecycle.

[REQUIRED] Link to test content

non-applicable

[REQUIRED] A full bug report captured from the device

no full report, since issue is observed on user's devices through Crashlytics

[REQUIRED] Version of ExoPlayer being used

e267550d95a72ba7261eddad6dea8f8ce379e8e8

[REQUIRED] Device(s) and version(s) of Android being used

According to crashlytics devices distribution looks like: 56% Xiaomi 20% Droidlogic 8% Amlogic 4% fengmi 12% Other (12)

Some of them :

System: 4.9.113 (20190809) 
Sdk: 28 
Device: galilei 
Product: galilei 
Manufacturer: AZW Model: GTKing 
Brand: Droidlogic
Model: X96Max_V311_P
Operating System Version: 9
Rooted: Yes
Brand: Droidlogic
Model: Tx5
Operating System Version: 9
Rooted: Yes
Brand: Amlogic
Model: TX5 Pro
Operating System Version: 8.1.0
Rooted: Yes
Brand: Xiaomi
Model: MiBox S
Operating System Version: 8.1.0
Rooted: No
Brand: Amlogic
Model: TX5 Pro
Operating System Version: 8.1.0
Rooted: Yes
Brand: NVIDIA
Model: SHIELD TV
Operating System Version: 9
Rooted: No
ojw28 commented 4 years ago

On first inspection, it appears this would be caused by MediaCodecVideoRenderer calling setOutputSurface with null. This appears to be impossible, however. There's only one call site and it's surround by a non-null check, here.

On closer inspection of android_media_MediaCodec_native_setSurface in the Android platform, it looks like this would actually be caused by the following block:

sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
    throwExceptionAsNecessary(env, INVALID_OPERATION);
    return;
}

I'm not really sure how this happens though.

@andrewlewis - Any ideas? @stari4ek - Please can you deobfuscate the stack traces properly, so we can see the proper method names and line numbers? Please can you also give an indication of absolute crash numbers. It's impossible to gauge how to prioritize issues just from percentages adding up to 100.

stari4ek commented 4 years ago
Non-fatal Exception: java.lang.IllegalArgumentException
       at android.media.MediaCodec.native_setSurface(MediaCodec.java)
       at android.media.MediaCodec.setOutputSurface + 1979(MediaCodec.java:1979)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setOutputSurfaceV23 + 1158(MediaCodecVideoRenderer.java:1158)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setSurface + 563(MediaCodecVideoRenderer.java:563)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.handleMessage + 529(MediaCodecVideoRenderer.java:529)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.deliverMessage + 976(ExoPlayerImplInternal.java:976)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageToTarget + 947(ExoPlayerImplInternal.java:947)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageInternal + 929(ExoPlayerImplInternal.java:929)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage + 363(ExoPlayerImplInternal.java:363)
       at android.os.Handler.dispatchMessage + 102(Handler.java:102)
       at android.os.Looper.loop + 193(Looper.java:193)
       at android.os.HandlerThread.run + 65(HandlerThread.java:65)
Non-fatal Exception: java.lang.IllegalStateException
       at android.media.MediaCodec.native_setSurface(MediaCodec.java)
       at android.media.MediaCodec.setOutputSurface + 1954(MediaCodec.java:1954)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setOutputSurfaceV23 + 1158(MediaCodecVideoRenderer.java:1158)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setSurface + 563(MediaCodecVideoRenderer.java:563)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.handleMessage + 529(MediaCodecVideoRenderer.java:529)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.deliverMessage + 976(ExoPlayerImplInternal.java:976)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageToTarget + 947(ExoPlayerImplInternal.java:947)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageInternal + 929(ExoPlayerImplInternal.java:929)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage + 363(ExoPlayerImplInternal.java:363)
       at android.os.Handler.dispatchMessage + 101(Handler.java:101)
       at android.os.Looper.loop + 164(Looper.java:164)
       at android.os.HandlerThread.run + 65(HandlerThread.java:65)

One more a bit different, but with same steps. setSurface(null)

Non-fatal Exception: android.media.MediaCodec$CodecException: Error 0x80000000
       at android.media.MediaCodec.native_setSurface(MediaCodec.java)
       at android.media.MediaCodec.setOutputSurface + 1797(MediaCodec.java:1797)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setOutputSurfaceV23 + 1158(MediaCodecVideoRenderer.java:1158)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.setSurface + 563(MediaCodecVideoRenderer.java:563)
       at com.google.android.exoplayer2.video.MediaCodecVideoRenderer.handleMessage + 529(MediaCodecVideoRenderer.java:529)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.deliverMessage + 976(ExoPlayerImplInternal.java:976)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageToTarget + 947(ExoPlayerImplInternal.java:947)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageInternal + 929(ExoPlayerImplInternal.java:929)
       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage + 363(ExoPlayerImplInternal.java:363)
       at android.os.Handler.dispatchMessage + 98(Handler.java:98)
       at android.os.Looper.loop + 148(Looper.java:148)
       at android.os.HandlerThread.run + 61(HandlerThread.java:61)

Brand: Skyworth
Model: Asia
Operating System Version: 6.0.1

Please can you also give an indication of absolute crash numbers. It's impossible to gauge how to prioritize issues just from percentages adding up to 100.

Stats for last month: "This issue has 1477 non-fatals affecting 675 users". (MAU is ~30k) It goes to playerFailed(), so no fatal crashes here.

It's hard to tell what exactly happens on device. Since it's TIF app, onSetSurface(null) is called when user leaves "Live TV"\"Live Channels". Also there is a chance that device is going to sleep.

Timings:

01:19:53.384 PM main|D] Session: onSetSurface: [null]
01:19:53.416 PM main|D] EventLogger: surfaceSizeChanged [16.61, 20.84, window=0, period=0, 0, 0]
01:19:53.430 PM main|T] Session: onSetSurface: 51ms
01:19:53.443 PM main|E] EventLogger: playerFailed [16.63, 20.84, window=0, period=0]
com.google.android.exoplayer2.ExoPlaybackException: java.lang.IllegalArgumentException 
  at com.google.android.exoplayer2.t.handleMessage(ExoPlayerImplInternal.java:24) 
  at android.os.Handler.dispatchMessage(Handler.java:102) 
  at android.os.Looper.loop(Looper.java:193) 
  at android.os.HandlerThread.run(HandlerThread.java:65) 
Caused by: java.lang.IllegalArgumentException: null
at android.media.MediaCodec.native_setSurface(Native Method) 
at android.media.MediaCodec.setOutputSurface(MediaCodec.java:1979) 
ojw28 commented 2 years ago

Sorry for the delayed response. Do you know if there's any adverse effect on the user from what you're seeing here? Playback is failing with the error you've provided details about, but the user is exiting the playback anyway, so I wonder whether they'd even notice anything.

It's still not ideal, for example from a metrics perspective if these end up being logged as failed playbacks, but at this point I don't think we have a good theory as to what's happening here.

stari4ek commented 2 years ago

Yeah. This issue just needs special handling on the app's side to be sure that user experience does not degrade (do not show any UI notifications about an error).

pbasista commented 1 year ago

The listed stack traces are very similar to the stack traces occurring during this issue: https://github.com/TeamNewPipe/NewPipe/issues/9023