Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Retry on error #7038

Merged
merged 9 commits into from
Mar 23, 2021
Merged

feat: Retry on error #7038

merged 9 commits into from
Mar 23, 2021

Conversation

alex-barstow
Copy link
Contributor

Description

This adds support for a retryOnError option that will have the player select a new source from the source list when the selected one fails prior to playback. If a source fails during playback, no retry will be attempted.

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why tests are failing, I would ignore that for now.

src/js/player.js Outdated
const retry = () => {
// Remove the error modal
this.error(null);
this.retrying_ = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible that we can re-use changingSrc_ here? It gets set to true when we set a new source and then is updated to false when a source gets set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't believe so. retrying_ signals that at least one retry has been attempted, so we don't add the removeRetryHandler below more than once during any subsequent retries. At the point when we need to check whether to add that handler or not, changingSrc_ is always true so it cannot serve the same purpose.

'last source remains'
);

player.dispose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a final check that verifies that player.currentSources() still contains the initial list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thinking

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

src/js/player.js Outdated
// Remove the error modal
this.error(null);
this.retrying_ = true;
this.src(sources.slice(1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did notice that we do the same thing above in the mdidleware setSource callback. I assume that that only happens when a middleware handler returns an error all the way down?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think that piece only is actually badly named. The err there is actually whether any techs can play the currently chosen source. So, this still makes sense. I think the other piece would trigger if we were to add say an RTMP source. Though, that's actually a good thing to verify that the two pieces don't step on each other's toes.

Copy link
Member

@misteroneill misteroneill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an offline discussion about the issue of differentiating between a src() call that comes from a user vs. one that comes from the retry process.

One potential avenue for that is to add another argument to src() but I think a preferable approach would be to use a pseudo-private method to hide the second argument. Something like:

src(source) {
  return this.src_(source);
}
src_(source, duringReset) {
  // Everything from current src() method, using duringReset as a 
  // differentiator and calling src_() internally.
}

I know src_() already exists, but I'm using it here for demonstration purposes.

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I did find an infinite error loop that's not great. Let me investigate it some more first.

src/js/player.js Outdated Show resolved Hide resolved
src/js/player.js Outdated Show resolved Hide resolved
src/js/player.js Outdated Show resolved Hide resolved
@gkatsev
Copy link
Member

gkatsev commented Jan 14, 2021

So, the infinite loop happens in this type of cases:

player.src([
  {src: 'http://vjs.zencdn.net/v/oceans.foo', type: 'video/mp4'},
  {src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8', type:'application/x-mpegurl'},
  {src: 'http://vjs.zencdn.net/v/oceans.mp4', type: 'video/mp4'},
  ]);
player.on('error', () => {
  console.log('we errored');
  player.src({src:'http://vjs.zencdn.net/v/oceans.bar', type:'video/mp4'})
});

If I listen to the error with one instead of on, everything is fine. Interestingly, giving it a second source works fine.
Looking at it some more, this would be an issue even without the retry on error. So, I don't think it's something we need to worry about here.

@video-archivist-bot
Copy link

Hey! We've detected some video files in a comment on this issue. If you'd like to permanently archive these videos and tie them to this project, a maintainer of the project can reply to this issue with the following commands:

@gkatsev
Copy link
Member

gkatsev commented Jan 14, 2021

So, with the following thing we end up with a player that works but I think it works because our error handlers aren't getting cleared up properly:

player.src([
  {src: './oceans.foo', type: 'video/mp4'},
  {src: './oceans.mp4', type: 'video/mp4'},
  {src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8', type:'application/x-mpegurl'},
  ]);
player.on('error', () => {
  console.log('iteration %d', ++i, DomData.get(player.el()).handlers.error.length);
  player.src([
    {src:'./oceans.bar', type:'video/mp4'},
    {src:'./oceans.baz', type:'video/mp4'},
  {src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8', type:'application/x-mpegurl'},
  ]);
});

I then added this line below where we add the error handler

      console.log('retry add', retry.guid)

and then the following before removal (in both playing handler and the reset method)

      console.log('retry remove', retry.guid)

I then end up with the following output:

18:42:03.600 video.js:26905 retry add 551
18:42:03.891 video.js:26905 retry add 1019
18:42:03.892 video.js:26913 retry remove 1019
18:42:03.893 video.js:26905 retry add 1021
18:42:03.936 video.js:26913 retry remove 1021
18:42:03.937 video.js:26905 retry add 1034
18:42:03.939 video.js:26905 retry add 1037
18:42:03.954 video.js:26913 retry remove 1037
18:42:03.955 video.js:26905 retry add 1043
18:42:03.957 video.js:26905 retry add 1046

Notice that 551, 1034, and 1046 haven't been removed. I'd expect that only 1046 wouldn't have a matching remove yet.
When I hit play, it seems like all those pending ones get removed as expected:

18:42:19.803 video.js:26899 retry remove 551
18:42:19.804 video.js:26899 retry remove 1034
18:42:19.804 video.js:26899 retry remove 1043

I think these extra error handlers end up making the above snippet work.

@video-archivist-bot
Copy link

Hey! We've detected some video files in a comment on this issue. If you'd like to permanently archive these videos and tie them to this project, a maintainer of the project can reply to this issue with the following commands:

@alex-barstow
Copy link
Contributor Author

I updated the reset logic so we remove all retry-related event handlers on every handleSrc_() call, and this seems to fix the cleanup issues.

Copy link
Member

@gkatsev gkatsev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally that the handlers get removed properly now and double-checked how this works for autoplay.
LGTM!

@gkatsev
Copy link
Member

gkatsev commented Jan 19, 2021

This PR fixes #1805!

@John2397
Copy link

John2397 commented Feb 1, 2021

This PR fixes #1805!

When can we expect the new release that will include this feature?

@gkatsev gkatsev merged commit 22e9843 into videojs:main Mar 23, 2021
@gkatsev
Copy link
Member

gkatsev commented May 19, 2021

@John2397 this is available now in v7.12.

@gkatsev
Copy link
Member

gkatsev commented May 19, 2021

Looks like we forgot to document this option in https://github.com/videojs/video.js/blob/main/docs/guides/options.md

tf pushed a commit to tf/video.js that referenced this pull request Feb 18, 2022
Add a `retryOnError` option. When set, during source selection, if a source fails to load, we will retry the next item in the sources list. In the future, we may enable this by default.

A source that fails during playback will *not* trigger this behavior.

Fixes videojs#1805.
edirub pushed a commit to edirub/video.js that referenced this pull request Jun 8, 2023
Add a `retryOnError` option. When set, during source selection, if a source fails to load, we will retry the next item in the sources list. In the future, we may enable this by default.

A source that fails during playback will *not* trigger this behavior.

Fixes videojs#1805.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants