diff --git a/lib/player.js b/lib/player.js index bcece6c63..dac6591c7 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2855,7 +2855,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget { return tracks; } else if (this.video_ && this.video_.src && this.video_.textTracks) { - const textTracks = Array.from(this.video_.textTracks); + const textTracks = Array.from(this.video_.textTracks) + .filter((t) => t.mode != 'disabled'); const StreamUtils = shaka.util.StreamUtils; return textTracks.map((text) => StreamUtils.html5TextTrackToTrack(text)); } else { diff --git a/lib/text/simple_text_displayer.js b/lib/text/simple_text_displayer.js index 72de4bffa..da2cac752 100644 --- a/lib/text/simple_text_displayer.js +++ b/lib/text/simple_text_displayer.js @@ -33,6 +33,8 @@ shaka.text.SimpleTextDisplayer = class { // If the video element has TextTracks, disable them. If we see one that // was created by a previous instance of Shaka Player, reuse it. for (const track of Array.from(video.textTracks)) { + // NOTE: There is no API available to remove a TextTrack from a video + // element. track.mode = 'disabled'; if (track.label == shaka.text.SimpleTextDisplayer.TextTrackLabel_) { @@ -145,6 +147,13 @@ shaka.text.SimpleTextDisplayer = class { if (this.textTrack_) { const removeIt = (cue) => true; shaka.text.SimpleTextDisplayer.removeWhere_(this.textTrack_, removeIt); + + // Prevent this extra TextTrack from affecting future playbacks. Without + // this, we get something bogus in the track list for src= playbacks, as + // in https://github.com/google/shaka-player/issues/2516 + // NOTE: There is no API available to remove a TextTrack from a video + // element. + this.textTrack_.mode = 'disabled'; } this.textTrack_ = null; diff --git a/test/player_src_equals_integration.js b/test/player_src_equals_integration.js index 3514dd62a..829acfbed 100644 --- a/test/player_src_equals_integration.js +++ b/test/player_src_equals_integration.js @@ -223,6 +223,14 @@ describe('Player Src Equals', () => { expect(player.getTextTracks()).toEqual([]); }); + it('ignores disabled text tracks on the video element', async () => { + const textTrack = video.addTextTrack('subtitles', 'label'); + textTrack.mode = 'disabled'; + + await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null); + expect(player.getTextTracks()).toEqual([]); + }); + // TODO: test HLS on platforms with native HLS it('allows selecting text tracks', async () => { await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null); diff --git a/test/text/simple_text_displayer_unit.js b/test/text/simple_text_displayer_unit.js index bc6141d2d..4dd1455bf 100644 --- a/test/text/simple_text_displayer_unit.js +++ b/test/text/simple_text_displayer_unit.js @@ -42,6 +42,10 @@ describe('SimpleTextDisplayer', () => { window.VTTCue = /** @type {?} */(FakeVTTCue); }); + afterEach(async () => { + await displayer.destroy(); + }); + afterAll(() => { window.VTTCue = originalVTTCue; }); @@ -296,6 +300,24 @@ describe('SimpleTextDisplayer', () => { }); }); + describe('destroy', () => { + it('disables the TextTrack it created', async () => { + // There should only be the one track created by this displayer. + expect(video.textTracks.length).toBe(1); + + /** @type {!TextTrack} */ + const textTrack = video.textTracks[0]; + + // It should not be disabled before we destroy it. + expect(textTrack.mode).not.toBe('disabled'); + + await displayer.destroy(); + + // It should be disabled after we destroy it. + expect(textTrack.mode).toBe('disabled'); + }); + }); + function createFakeCue(startTime, endTime) { return {startTime: startTime, endTime: endTime}; }