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

Fix captions disappear on unbuffered seeks when user activates, deactivates, and reactivates captions #2674

Merged

Conversation

muhammadharis
Copy link
Contributor

@muhammadharis muhammadharis commented Jun 22, 2020

What is the bug?
#2671

Why does this bug exist?
When the subtitles are turned off, unloadTextStream() on streaming engine is called, but currentTextStream is never set to null. When the captions are turned back on, a check in player.js sees that the current text stream is not null, so it assumes it exists, and is ready to go. Thus captions are never loaded.

What is the fix?
Clearing (setting to null) the current stream on unloadTextStream ensures the text stream is properly initialized the next time it is called, and in a state so that captions can resume being parsed.

Notes
After further investigation, it seems that the unit tests are written in a way that assumes that the text stream is nulled when unloadTextStream is called. So this fix is definitely something that we assume already happens.

Copy link
Member

@joeyparrish joeyparrish left a comment

Choose a reason for hiding this comment

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

This looks great. Any chance you could try adding a test for this? Maybe player_unit.js?

@muhammadharis
Copy link
Contributor Author

muhammadharis commented Jun 23, 2020

@joeyparrish While further investigating this issue, I found a more elegant solution, and one that, from the way the code and unit tests are currently written, it seems that we assume already exists. I've described it in the edited summary of the pull request.

Furthermore, I found another bug (which I now fixed) while fixing this issue, where captions show up in 2 different languages simultaneously when turned off, then back on in a different language. This happens because the UI library calls setTextTrackVisibility then selectTextLanguage, which both try to switch the text stream to different languages. However, if we reverse the order in which they are called, then when setTextTrackVisibility is called after selectTextLanguage, it will see that a language is already selected, and won't try to switch the text stream, fixing the issue!

@@ -251,6 +251,7 @@ shaka.media.StreamingEngine = class {
this.abortOperations_(state).catch(() => {});
this.mediaStates_.delete(ContentType.TEXT);
}
this.currentTextStream_ = null;
Copy link
Member

Choose a reason for hiding this comment

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

Oh, wow, that certainly should have been there. :-)

This member is new in v3.0 if I recall correctly.

@@ -341,6 +341,19 @@ describe('Player', () => {
expect(streamingEngine.switchTextStream).not.toHaveBeenCalled();
expect(streamingEngine.unloadTextStream).not.toHaveBeenCalled();
});

it('unloads text stream when captions are turned off', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

It seems that this test only verifies that unloadTextStream() is called, but your fix was to make sure that getCurrentTextStream() returns null after unloadTextStream().

So instead of verifying the call to unloadTextStream() happened (which is an implementation detail), I think you should be verifying the value of getCurrentTextStream() at the end (which is the observable behavior in the rest of the system).

Comment on lines 167 to 170
if (this.player) { // May have become null while awaiting
this.player.selectTextLanguage(track.language, track.roles[0]);
}
await this.player.setTextTrackVisibility(true);
Copy link
Member

Choose a reason for hiding this comment

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

Moving the visibility change after selectTextLanguage() removes the need to check for this.player. We know it's non-null at the start of the method, but it may become null in the middle of an async call if someone calls destroy().

But I'm also not entirely sure why you changed this. Can you explain?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, sorry, now I see your comment from before:

I found another bug (which I now fixed) while fixing this issue, where captions show up in 2 different languages simultaneously when turned off, then back on in a different language. This happens because the UI library calls setTextTrackVisibility then selectTextLanguage, which both try to switch the text stream to different languages. However, if we reverse the order in which they are called, then when setTextTrackVisibility is called after selectTextLanguage, it will see that a language is already selected, and won't try to switch the text stream, fixing the issue!

Then please remove the if (this.player) above and leave a comment explaining why these calls need to be in this order. Otherwise, we may change it back some day without realizing we broke anything. (Even better would be an automated test for this, but I would accept a strongly-worded comment in this case.)

Copy link
Member

@joeyparrish joeyparrish 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 otherwise. Thanks!

await player.setTextTrackVisibility(true);
await player.load(fakeManifestUri, 0, fakeMimeType);
expect(streamingEngine.switchTextStream).toHaveBeenCalled();
expect(shaka.test.Util.invokeSpy(streamingEngine.getCurrentTextStream))
Copy link
Member

Choose a reason for hiding this comment

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

The invokeSpy wrapper is a compiler hack since the compiler doesn't see jasmine Spy objects as callable. But in this case, I believe this method is not a Spy, so you should be able to remove the wrapper and call it directly. Same below.

If I'm wrong, as I often am, build/check.py would complain after removing the wrapper.

Copy link
Contributor Author

@muhammadharis muhammadharis Jun 24, 2020

Choose a reason for hiding this comment

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

I just verified, and it seems like there is a Jasmine Spy created for this method in simple_fakes.js (https://github.com/google/shaka-player/blob/master/test/test/util/simple_fakes.js#L103). Consequently, the compiler complains when I remove the wrapper and call the method directly.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see. This is player_unit, so streamingEngine is a fake. Now the spy part makes more sense.

But since the fix is in StreamingEngine, testing with a fake StreamingEngine doesn't verify the fix. :-) You could comment out your one-line fix, and this test would still pass.

You'll need to test this in test/media/streaming_engine_unit.js instead.

@shaka-bot
Copy link
Collaborator

All tests passed!

@joeyparrish joeyparrish merged commit d53fbae into shaka-project:master Jun 24, 2020
joeyparrish pushed a commit that referenced this pull request Jul 27, 2020
When the subtitles are turned off, unloadTextStream() on streaming engine is called, but currentTextStream is never set to null. When the captions are turned back on, a check in player.js sees that the current text stream is not null, so it assumes it exists, and is ready to go. Thus captions are never loaded.

Clearing (setting to null) the current stream on unloadTextStream ensures the text stream is properly initialized the next time it is called, and in a state so that captions can resume being parsed.

After further investigation, it seems that the unit tests are written in a way that assumes that the text stream is nulled when unloadTextStream is called. So this fix is definitely something that we assume already happens.

Closes #2671
@github-actions github-actions bot added the status: archived Archived and locked; will not be updated label Jul 25, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: archived Archived and locked; will not be updated
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants