ryanheise / just_audio

Audio Player
1.05k stars 676 forks source link

Errors not reported + stuck in playing when `preload: false` #390

Open kmod-midori opened 3 years ago

kmod-midori commented 3 years ago

Which API doesn't behave as documented, and how does it misbehave? player.setAudioSource with preload: false, initialIndex: .... When trying to play an invalid URL in this way, errors are never reported to the application and the player does not automatically jump to the next item.

Minimal reproduction project https://github.com/chengyuhui/just_audio/tree/error-flood Replaced URLs to invalid ones with additional change on L81, disabling preload with initialIndex.

To Reproduce (i.e. user steps, not code) Steps to reproduce the behavior:

  1. Launch the example
  2. Tap play
  3. See that the player is stuck in playing state
  4. Tap the third item (which is also invalid)
  5. See that the player also stucks in playing state, with nothing actually playing

Error messages

Launching lib/main.dart on MIX 2S in debug mode...
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:34255/k5grn-W0nnY=/ws
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe; (greylist,core-platform-api, linking, allowed)
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J (greylist,core-platform-api, linking, allowed)
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z (greylist, linking, allowed)
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed)
I/t_audio_exampl(19603): ProcessProfilingInfo new_methods=523 is saved saved_to_disk=1 resolve_classes_delay=8000
I/flutter (19372): core onAudioFocusChanged
W/t_audio_exampl(19603): Accessing hidden method Landroid/media/AudioTrack;->getLatency()I (greylist, reflection, allowed)
I/ExoPlayerImpl(19603): Init 9e73da9 [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29]
D/AudioManager(19603): getStreamVolume isRestricted mode = 0
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z (greylist, linking, allowed)
E/ExoPlayerImplInternal(19603): Playback error
E/ExoPlayerImplInternal(19603):   com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:579)
E/ExoPlayerImplInternal(19603):       at android.os.Handler.dispatchMessage(Handler.java:103)
E/ExoPlayerImplInternal(19603):       at android.os.Looper.loop(Looper.java:228)
E/ExoPlayerImplInternal(19603):       at android.os.HandlerThread.run(HandlerThread.java:67)
E/ExoPlayerImplInternal(19603):   Caused by: com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:356)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:201)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1015)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415)
E/ExoPlayerImplInternal(19603):       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal(19603):       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal(19603):       at java.lang.Thread.run(Thread.java:919)
E/ExoPlayerImplInternal(19603):   Caused by: java.net.ConnectException: Failed to connect to /127.0.0.1:443
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:90)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:30)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:641)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:543)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:349)
E/ExoPlayerImplInternal(19603):       ... 7 more
E/AudioPlayer(19603): TYPE_SOURCE: Unable to connect
I/ExoPlayerImpl(19603): Release 9e73da9 [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29] [goog.exo.core]
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z (greylist, linking, allowed)
Reloaded 1 of 802 libraries in 986ms.
I/flutter (19372): ### Stop
I/ExoPlayerImpl(19603): Init c9886bb [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29]
D/AudioManager(19603): getStreamVolume isRestricted mode = 0
E/ExoPlayerImplInternal(19603): Playback error
E/ExoPlayerImplInternal(19603):   com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:579)
E/ExoPlayerImplInternal(19603):       at android.os.Handler.dispatchMessage(Handler.java:103)
E/ExoPlayerImplInternal(19603):       at android.os.Looper.loop(Looper.java:228)
E/ExoPlayerImplInternal(19603):       at android.os.HandlerThread.run(HandlerThread.java:67)
E/ExoPlayerImplInternal(19603):   Caused by: com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:356)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:201)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1015)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415)
E/ExoPlayerImplInternal(19603):       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal(19603):       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal(19603):       at java.lang.Thread.run(Thread.java:919)
E/ExoPlayerImplInternal(19603):   Caused by: java.net.ConnectException: Failed to connect to /127.0.0.1:443
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:90)
E/ExoPlayerImplInternal(19603):       at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:30)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:641)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:543)
E/ExoPlayerImplInternal(19603):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:349)
E/ExoPlayerImplInternal(19603):       ... 7 more
E/AudioPlayer(19603): TYPE_SOURCE: Unable to connect
I/ExoPlayerImpl(19603): Release c9886bb [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29] [goog.exo.core]
W/t_audio_exampl(19603): Accessing hidden method Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z (greylist, linking, allowed)

Notice that neither A stream error occurred nor Error loading playlist is here.

Expected behavior The player should advance to the next item, and this error should be added to playbackEventStream or thrown.

Smartphone (please complete the following information):

Flutter SDK version

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.4, on Linux, locale zh_CN.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Chrome - develop for the web
[✓] Android Studio
[✓] IntelliJ IDEA Ultimate Edition (version 2020.3)
[✓] Connected device (2 available)

Additional context The whole error handling situation seems to be hit-or-miss, maybe we need some general way to improve it?

ryanheise commented 3 years ago

I think it's reasonable that errors aren't reported in the initial call if you requested it not to load, but you should still be able to catch the error either by a later explicit call to load or by listening to errors on the playbackState stream.

Is is unexpected that it doesn't automatically skip to the next item though.

kmod-midori commented 3 years ago

The fact that the error is never reported to playbackStateStream and automatic skip does not work suggests that the Java side of the plugin might also missed the exception.

ryanheise commented 3 years ago

Sorry I realised I should have said the playbackEventStream.

kmod-midori commented 3 years ago

That one does not work either.

kmod-midori commented 3 years ago

https://github.com/ryanheise/just_audio/blob/e37b50d6cd6fcca8108cf083ff3bddd400c37acc/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java#L250-L265 Since we have E/AudioPlayer(19603): TYPE_SOURCE: Unable to connect, it is sure that we have reached there, and the stream listener has been registered on the Dart side (verified with breakpoints). So the error message somehow got lost in the channel?

After these lost errors, the player is unable to play anything when seeked to, even the valid files. It do start working on valid files if I pause and play again, and errors are reported normally from now on. This bug seems only happen if the initial file is unplayable?

ryanheise commented 3 years ago

sendError only sends the error if the event sink is not null. It's unlikely it would be null though, it would mean it tries to send an error too soon, before the event sink is initialised. On the other end, the error won't be received unless you're already listening to it before the error happens. Again, probably unlikely for this to go wrong either but there are the first two things I would check. Otherwise, maybe there's an unlogged exception happening somewhere within the plugin where it's crashing silently added failing to deliver the sent error. In that case the code needs extra try/catch logging.

kmod-midori commented 3 years ago

sendError is very likely actually called, but I'm unsure if the event sink is fine. In that case I'll need to debug the Android side then. Is this possible in Android Studio? I usually use VSCode for all my Flutter works.

ryanheise commented 3 years ago

They're all just text files after all. I use plain old Vim to edit everything so any other editor that can edit files should work.

kmod-midori commented 3 years ago

Further debugging shows that sendError is indeed called and the eventSink is not null. So for some reason the error did not get through?

Putting breakpoint on the onError callback of platform.playbackEventMessageStream yields nothing, indicating that it is not called.

The player only returns to normal if I:

kmod-midori commented 3 years ago

If I tell Android Studio to pause on all exceptions, the connection error caused by connecting to the invalid URL keeps popping up, which means that the player looks never stopped (but it is released with Release c9886bb [ExoPlayerLib/2.13.1] [polaris, MIX 2S, Xiaomi, 29] [goog.exo.core]?)