Skip to content
This repository was archived by the owner on Sep 8, 2021. It is now read-only.

Fix remaining issues with playback stopping intermittently #1080

Merged

Conversation

fxthomas
Copy link
Contributor

This is a follow-up of #1035 and #685, and I would love some testers on this!

@tari's patch helped a lot, but I was still seeing intermittent playback failures when switching songs, though not always and not with the same message...

I'm now fairly sure that this is caused by some of our JS code trying to run play() while the player is still loading the stream, causing a race condition.

I experimented a bit with the skip(index, position) function at the core of the play queue and added some logging to the player. I came to the conclusion that some methods (I identified player.play(), player.load() or setting player.currentTime) will automatically start loading the stream in the background, and calling any of them while the player is still loading will crash and burn.

The following screenshot shows the player load a first stream, then a a second (see both logs from playQueue.view) ; the request is then interrupted by something else and the player does not try again.

Screenshot from 2019-05-24 00-52-30

The following patch makes sure that all code paths in skip load AND start the stream exactly once, and then start playback when the canplay event fires. This appears to fix everything for me. Tested on FF 68 and Chromium 74.

@muff1nman
Copy link
Contributor

muff1nman commented May 24, 2019

Got some AbortError in Firefox, but the next song started playing just fine. Not sure what its abortin as it happens after the pause and ended events.

Received audio event: play playQueue.view:226:56
play { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 248967, … }
playQueue.view:226:101
Received audio event: playing playQueue.view:227:59
playing { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 248975, … }
playQueue.view:227:107
Server side script session id timed out. New session automatically created engine.js:1:8190
Received audio event: pause playQueue.view:228:57
pause { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412222, … }
playQueue.view:228:103
https://www.xxxxxx.com/airsonic/stream?player=79&id=11686 playQueue.view:640:17
Received audio event: ended playQueue.view:229:57
ended { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412225, … }
playQueue.view:229:103
AbortError: The operation was aborted. 
Received audio event: loadedmetadata playQueue.view:222:66
loadedmetadata { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412680, … }
playQueue.view:222:121
Received audio event: canplay playQueue.view:221:59
canplay { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412683, … }
playQueue.view:221:107
Received audio event: canplay playQueue.view:225:59
canplay { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412683, … }
playQueue.view:225:107
Received audio event: play playQueue.view:226:56
play { target: null, isTrusted: false, detail: {…}, eventPhase: 0, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, timeStamp: 412689, … }
playQueue.view:226:101
Received audio event: playing

@muff1nman
Copy link
Contributor

The AbortError might be due to the fact that currentTime is changed right after loading a new song as aluded to here: https://bugzilla.mozilla.org/show_bug.cgi?id=1507193#c4.

Why change currentTime to position when loading a new song?

@muff1nman
Copy link
Contributor

One thing I'm still experiencing is abrupt skips. I.e. the next song starts playing before the current one is over. I wonder if that's because I'm on a degraded network separate from the server and its missing data during which the onended event is fired.

This link maybe supports the above hypothesis: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event

The ended event is fired when playback or streaming has stopped because the end of the media was reached or because no further data is available. This event occurs based upon HTMLMediaElement ( and

However I think this behavior isn't directly related to playback stopping which I have yet to see with this patch.

@muff1nman
Copy link
Contributor

Tangent: Looks like not providing content length for transcoded music does the following in chrome: image

Not sure why mediaelement doens't do that in firefox.

@fxthomas
Copy link
Contributor Author

fxthomas commented May 24, 2019

Re: changing currentTime, we currently set it every time, which unfortunately also starts loading the stream. It's a parameter for skip, so I don't like to ignore it, but at least this patch avoids setting it when there's no need to.

Re:Chrome appearance, I noticed the same thing. I usually use FF, and it only flashes "Live Broadcast" for a fraction of a second before showing the progress bar. (I personally like having the progress bar, but it's not related to the PR, I think.)

Re: the AbortError you saw, can you check that the data streamed from Airsonic is complete? Did you check media files with transcoding or without? I can reliably reproduce the issues I experience with FLAC files, less so with files already in MP3.

@chaosbiber
Copy link

This PR has been working fine for me, but I have only compared it to 10.3.1.
After the last release update the playback stopped between every song in Firefox, accompanied by a "Client unexpectedly closed connection" server message. Chrome behaviour improved with 10.3.1 but with the state of this PR both browsers have been playing different files without stopping. I've also noticed the "Live Broadcast" instead of the bar in Chrome for transcoded files but working playback is more important. No AbortErrors for me.

@fxthomas fxthomas force-pushed the try-to-fix-playback-intermittent-stops branch from 8663c8f to e8fdb50 Compare June 4, 2019 20:40
@fxthomas fxthomas changed the title WIP: Try to fix remaining issues with playback stopping intermittently Fix remaining issues with playback stopping intermittently Jun 4, 2019
@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 4, 2019

Nice, thank you very much for testing this! I haven't seen any playback errors anymore since using this either, so we're on a good track.

All: I rebased/edited the commit bit to make that ready for merging, feel free to suggest improvements.

@jvoisin
Copy link
Contributor

jvoisin commented Jun 4, 2019

Shouldn't this be fixed upstream in mediaelement.js?

@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 4, 2019

Not sure, we were really using it incorrectly before this commit.

And implementing the promises API correctly is probably a big change considering how MEJS works right now.

@jvoisin
Copy link
Contributor

jvoisin commented Jun 4, 2019

I guess we can get this merged as soon as you clean the comments up?

@fxthomas fxthomas force-pushed the try-to-fix-playback-intermittent-stops branch from e8fdb50 to e3c661e Compare June 4, 2019 21:46
@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 4, 2019

Yup, go ahead!

@jvoisin
Copy link
Contributor

jvoisin commented Jun 4, 2019

As soon as you remove the comments :P
Or you want me to do it?

@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 5, 2019

Ah, I thought you meant the PR comments!

The fact that MEJS had a completely different interface than HTML5 ME threw me off at the beginning, I'd rather leave a big comment explaining that. I'll remove the debug lines but leave the rest, does that sound good?

@jooola
Copy link
Contributor

jooola commented Jun 5, 2019

Would be nice to ship this asap in 10.3.2 because it is getting really annoying!

@jooola
Copy link
Contributor

jooola commented Jun 5, 2019

I just tested this PR + the current state of master @ 4f59c72 .
It is working but same as above, the browser console is raising an error: AbortError: The operation was aborted.

On Firefox 67.0, debian 10

@jvoisin
Copy link
Contributor

jvoisin commented Jun 5, 2019

But comments, in mean the commented debug lines, not the useful ones, sorry :)

@jooola Could it be a caching issue in your browser?

@jooola
Copy link
Contributor

jooola commented Jun 5, 2019

@jooola Could it be a caching issue in your browser?

Nope, tried again with a fresh browser.

@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 5, 2019 via email

@jooola
Copy link
Contributor

jooola commented Jun 5, 2019

I only removed the flac transcoding since it was broken, I will add it back.

I'll check this with your tool (if it exists on Linux).

But even with flac disabled it shouldn't raise anything right ?

@fxthomas fxthomas force-pushed the try-to-fix-playback-intermittent-stops branch from e3c661e to 3a537f4 Compare June 5, 2019 18:55
@fxthomas
Copy link
Contributor Author

fxthomas commented Jun 5, 2019

If the original MP3 file is corrupted (even if it's very slightly, at the end) I think this error can appear. At least Debian (https://packages.debian.org/source/sid/mp3check) and Arch (https://aur.archlinux.org/packages/mp3check/) have packages. Let me know if you find something!

Anyway, debug lines removed!

@jooola
Copy link
Contributor

jooola commented Jun 5, 2019

So I checked my mp3 file and the only error I get is:
31967 bytes of junk before first frame header

But I also noticed that if the song isn't played long enough everything is working fine. Also the error happen on flac files, even if trans-coding for flac has been disabled.

#902 (comment)

@fxthomas
Copy link
Contributor Author

fxthomas commented Jul 1, 2019

I didn't have a lot of time to work on this last week, real life happened ;)

I am confident in the first commit (been running my Airsonic instance with it for a few weeks now), what do you think about creating a new PR with it as a workaround? This PR can stay as our main testing branch until we manage to completely fix this in MEJS.

@muff1nman
Copy link
Contributor

Why not merge this entire PR? We can fix the MEJS thing later as the firefox AbortError doesn't prevent smooth playback.

@Adnn
Copy link

Adnn commented Jul 2, 2019

After building latest master with fxthomas:try-to-fix-playback-intermittent-stops merged in, I have been listening to a few albums and did not have any problem with next song not playing.

A strange behaviour I am observing is that the last song of the list is repeating (i.e., as soon as it is finished, it restarts from the beginning).

@fxthomas
Copy link
Contributor Author

fxthomas commented Jul 2, 2019

@Adnn 5e406f0 was supposed to fix it, but apparently it's still a bit racey and doesn't work everytime (including for me).

@muff1nman @jvoisin I'll remove the tentative fix for the loop issue @Adnn was talking about as well as the debug logs in the JS console, and we can merge it.

@fxthomas fxthomas force-pushed the try-to-fix-playback-intermittent-stops branch from 8e3ba43 to 5183741 Compare July 2, 2019 20:13
fxthomas added 2 commits July 2, 2019 22:21
This commit works around a race caused by some of our JS code trying to
run play() for the next song while the MEJS player is still cleaning up
the last song.

MEJS issue: mediaelement/mediaelement#2650
This commit is kind of a hack to force MediaElement.js to show a
progress bar when we are loading a song.

Normally, in vanilla HTML5 MediaElement, we'd explicitely call 'play()'
when loading a song. Here, we cannot ensure playback will work well if
we don't wait for the 'canplay' event to be fired, but the progress bar
won't show up without 'play()'...

This commit emits a fake 'waiting' event to let the MEJS know that it
should update the UI.
@fxthomas fxthomas force-pushed the try-to-fix-playback-intermittent-stops branch from 5183741 to 2e01342 Compare July 2, 2019 20:23
@fxthomas
Copy link
Contributor Author

fxthomas commented Jul 2, 2019

Okay, ready to merge as soon as Travis finishes testing it!

@fxthomas fxthomas merged commit c834bde into airsonic:master Jul 2, 2019
@fxthomas fxthomas deleted the try-to-fix-playback-intermittent-stops branch July 2, 2019 23:04
@RedGreen007
Copy link

So...as the resident noob to Github, does this mean that I can now go back to the base airsonic...repo? Am I using words right? hahahahahaha

I am still running your test branch and just been following this thread. :p

@muff1nman
Copy link
Contributor

It means you can use the master branch now. However there is no release with this change yet so you'd need to build master yourself.

@RedGreen007
Copy link

Perfect. Thank you 👍

@RedGreen007
Copy link

Still stopping with Firefox. Works fine in Edge and Chrome. Also got an error while compiling. Had to shut off testing.

@jvoisin
Copy link
Contributor

jvoisin commented Jul 4, 2019

Please do tell us more about this error

@RedGreen007
Copy link

RedGreen007 commented Jul 7, 2019

[ERROR] Failures:
[ERROR]   SearchServiceTestCase.testSearchTypical:237 (15) Specify album 'Ravel - Chamber Music', and get songs. The first song is  expected:<0[1 - Gaspard de la Nuit - i. Ondine]> but was:<0[2 - Gaspard de la Nuit - ii. Le Gibet]>
[INFO]
[ERROR] Tests run: 161, Failures: 1, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for Airsonic 10.4.0-SNAPSHOT:
[INFO]
[INFO] Airsonic ........................................... SUCCESS [  2.579 s]
[INFO] Subsonic REST API .................................. SUCCESS [ 13.869 s]
[INFO] Sonos API .......................................... SUCCESS [ 15.400 s]
[INFO] Airsonic Main ...................................... FAILURE [  01:06 h]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:06 h
[INFO] Finished at: 2019-07-07T15:35:48-07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.0:test (default-test) on project airsonic-main: There are test failures.
[ERROR]
[ERROR] Please refer to /home/pi/airsonic/airsonic-main/target/surefire-reports for the individual test results.
[ERROR] Please refer to dump files (if any exist) [date]-jvmRun[N].dump, [date].dumpstream and [date]-jvmRun[N].dumpstream.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <goals> -rf :airsonic-main

I bypassed it by running mvn clean package -DskipTests

Sorry had to run it again to get the error.

@jvoisin
Copy link
Contributor

jvoisin commented Jul 8, 2019

Your stacktrace looks completely unrelated to the playback issue.
Please open an other issue about it.

@RedGreen007
Copy link

I think that worked hahaha. Sorry I just wanted to post it here cause it happened right after the changes got put back into the master. I wasn't sure if it was related. I just hit create new issue and copy and pasted the error there.

fxthomas added a commit to fxthomas/airsonic that referenced this pull request Sep 21, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
fxthomas added a commit to fxthomas/airsonic that referenced this pull request Sep 21, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
fxthomas added a commit to fxthomas/airsonic that referenced this pull request Sep 21, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
fxthomas added a commit to fxthomas/airsonic that referenced this pull request Sep 21, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
fxthomas added a commit to fxthomas/airsonic that referenced this pull request Sep 22, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
@fxthomas
Copy link
Contributor Author

fxthomas commented Sep 23, 2019

If some of you can go over at #1254 and let me know if you encounter playback issues with it, I would appreciate it. I don't have any on my machines, but additional testers are nice!

tesshucom pushed a commit to tesshucom/airsonic that referenced this pull request Oct 10, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
fxthomas added a commit to fxthomas/airsonic that referenced this pull request Oct 12, 2019
This commit is hopefully the final fix on Airsonic's side for airsonic#685.  It
also fixes airsonic#1160, which was caused by temporary workarounds introduced
in airsonic#1080 while we were looking for a solution.

The root cause of the issue is the fact that, when we go to the next
track in an Airsonic play queue, we change the media source in the
`ended` event.

In MEJS, this translates as the following two things:

* In Airsonic's 'ended' event, we change the media source (set the `src`
  attribute) and call the `load()` method, followed by the `play()`
  method.

* The 'ended' event was also used internally by the MEJS player, and
  one of these internal uses called the `pause()` method (presumably in
  order to make sure that playback was stopped on some media renderers).

Unfortunately, the order in which these events are called depends (in
all modern browsers) on the order in which they are registered.

In our case, the first one is registered inside the `<body>` tag, but
the second one is registered with `$(document).ready(...)`. This means
that the first event handler is called before the second.

This means that, in some cases (when we're unlucky, hence the seemingly
random nature of the bug), `pause()` is called after `load()`
but before the media has finished loading.

Apparently, this causes the `AbortError: The fetching process for the
media resource was aborted by the user agent at the user's request.`
message to appear (which indicates exactly what's described in the last
paragraph), and the playback of the next song is aborted.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.