From a543a0aaa4c076570a69e616abea418bf6959e81 Mon Sep 17 00:00:00 2001 From: David Streeter Date: Wed, 15 Mar 2023 12:46:43 -0400 Subject: [PATCH 1/2] fix for #5292 - push to filterSubtitleTracks output if kind is captions --- src/controller/subtitle-track-controller.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controller/subtitle-track-controller.ts b/src/controller/subtitle-track-controller.ts index 2366af75704..2fb54d66661 100644 --- a/src/controller/subtitle-track-controller.ts +++ b/src/controller/subtitle-track-controller.ts @@ -425,7 +425,10 @@ function filterSubtitleTracks(textTrackList: TextTrackList): TextTrack[] { for (let i = 0; i < textTrackList.length; i++) { const track = textTrackList[i]; // Edge adds a track without a label; we don't want to use it - if (track.kind === 'subtitles' && track.label) { + if ( + (track.kind === 'subtitles' || track.kind === 'captions') && + track.label + ) { tracks.push(textTrackList[i]); } } From 31fc88ad9b50ff5e031e003be46734bdae9e5c0d Mon Sep 17 00:00:00 2001 From: David Streeter Date: Fri, 17 Mar 2023 10:56:34 -0400 Subject: [PATCH 2/2] adding "captions" to all subtitle-track-controller tests --- .../controller/subtitle-track-controller.js | 575 +++++++++--------- 1 file changed, 294 insertions(+), 281 deletions(-) diff --git a/tests/unit/controller/subtitle-track-controller.js b/tests/unit/controller/subtitle-track-controller.js index fa36f501c97..fc948f87a63 100644 --- a/tests/unit/controller/subtitle-track-controller.js +++ b/tests/unit/controller/subtitle-track-controller.js @@ -5,338 +5,351 @@ import { LoadStats } from '../../../src/loader/load-stats'; import { LevelDetails } from '../../../src/loader/level-details'; import { Events } from '../../../src/events'; -describe('SubtitleTrackController', function () { - let subtitleTrackController; - let videoElement; - let hls; - let sandbox; - let fake; - - beforeEach(function () { - sandbox = sinon.createSandbox(); - fake = sinon.useFakeXMLHttpRequest(); - hls = new Hls({ - renderNatively: true, - }); - - videoElement = document.createElement('video'); - subtitleTrackController = new SubtitleTrackController(hls); - subtitleTrackController.media = videoElement; - subtitleTrackController.tracks = subtitleTrackController.tracksInGroup = [ - { - id: 0, - groupId: 'default-text-group', - lang: 'en', - name: 'English', - type: 'SUBTITLES', - url: 'baz', - details: { live: false }, - }, - { - id: 1, - groupId: 'default-text-group', - lang: 'en', - name: 'English', - type: 'SUBTITLES', - url: 'bar', - }, - { - id: 2, - groupId: 'default-text-group', - lang: 'en', - name: 'English', - type: 'SUBTITLES', - url: 'foo', - details: { live: true }, - }, - ]; - - const textTrack1 = videoElement.addTextTrack('subtitles', 'English', 'en'); - const textTrack2 = videoElement.addTextTrack('subtitles', 'Swedish', 'se'); - textTrack1.groupId = 'default-text-group'; - textTrack2.groupId = 'default-text-group'; - subtitleTrackController.groupId = 'default-text-group'; - - textTrack1.mode = 'disabled'; - textTrack2.mode = 'disabled'; - }); - - afterEach(function () { - hls.destroy(); - sandbox.restore(); - fake.restore(); - }); - - describe('onTextTrackChanged', function () { - it('should set subtitleTrack to -1 if disabled', function () { - expect(subtitleTrackController.subtitleTrack).to.equal(-1); - - videoElement.textTracks[0].mode = 'disabled'; - subtitleTrackController.onTextTracksChanged(); - - expect(subtitleTrackController.subtitleTrack).to.equal(-1); - }); - - it('should set subtitleTrack to 0 if hidden', function () { - expect(subtitleTrackController.subtitleTrack).to.equal(-1); - - videoElement.textTracks[0].mode = 'hidden'; - subtitleTrackController.onTextTracksChanged(); - - expect(subtitleTrackController.subtitleTrack).to.equal(0); - }); - - it('should set subtitleTrack to 0 if showing', function () { - expect(subtitleTrackController.subtitleTrack).to.equal(-1); - - videoElement.textTracks[0].mode = 'showing'; - subtitleTrackController.onTextTracksChanged(); - - expect(subtitleTrackController.subtitleTrack).to.equal(0); - }); - }); - - describe('set subtitleTrack', function () { - it('should set active text track mode to showing', function () { - videoElement.textTracks[0].mode = 'disabled'; - - subtitleTrackController.subtitleDisplay = true; - subtitleTrackController.subtitleTrack = 0; - - expect(videoElement.textTracks[0].mode).to.equal('showing'); - }); - - it('should set active text track mode to hidden', function () { - videoElement.textTracks[0].mode = 'disabled'; - subtitleTrackController.subtitleDisplay = false; - subtitleTrackController.subtitleTrack = 0; - - expect(videoElement.textTracks[0].mode).to.equal('hidden'); - }); - - it('should disable previous track', function () { - // Change active track without triggering setSubtitleTrackInternal - subtitleTrackController.trackId = 0; - // Change active track and trigger setSubtitleTrackInternal - subtitleTrackController.subtitleTrack = 1; - - expect(videoElement.textTracks[0].mode).to.equal('disabled'); - }); - - it('should trigger SUBTITLE_TRACK_SWITCH', function () { - const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); - subtitleTrackController.canLoad = true; - subtitleTrackController.trackId = 0; - subtitleTrackController.subtitleTrack = 1; +['subtitles', 'captions'].forEach(function (captionType) { + describe('SubtitleTrackController', function () { + let subtitleTrackController; + let videoElement; + let hls; + let sandbox; + let fake; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + fake = sinon.useFakeXMLHttpRequest(); + hls = new Hls({ + renderNatively: true, + }); - expect(triggerSpy).to.have.been.calledTwice; - expect(triggerSpy.firstCall).to.have.been.calledWith( - 'hlsSubtitleTrackSwitch', + videoElement = document.createElement('video'); + subtitleTrackController = new SubtitleTrackController(hls); + subtitleTrackController.media = videoElement; + subtitleTrackController.tracks = subtitleTrackController.tracksInGroup = [ { - id: 1, + id: 0, groupId: 'default-text-group', + lang: 'en', name: 'English', type: 'SUBTITLES', - url: 'bar', - } - ); - }); - - it('should trigger SUBTITLE_TRACK_LOADING if the track has no details', function () { - const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); - subtitleTrackController.canLoad = true; - subtitleTrackController.trackId = 0; - subtitleTrackController.subtitleTrack = 1; - - expect(triggerSpy).to.have.been.calledTwice; - expect(triggerSpy.secondCall).to.have.been.calledWith( - 'hlsSubtitleTrackLoading', + url: 'baz', + details: { live: false }, + }, { - url: 'bar', id: 1, groupId: 'default-text-group', - deliveryDirectives: null, - } - ); - }); - - it('should not trigger SUBTITLE_TRACK_LOADING if the track has details and is not live', function () { - const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); - subtitleTrackController.trackId = 1; - subtitleTrackController.subtitleTrack = 0; - - expect(triggerSpy).to.have.been.calledOnce; - expect(triggerSpy.firstCall).to.have.been.calledWith( - 'hlsSubtitleTrackSwitch', + lang: 'en', + name: 'English', + type: 'SUBTITLES', + url: 'bar', + }, { - id: 0, + id: 2, groupId: 'default-text-group', + lang: 'en', name: 'English', type: 'SUBTITLES', - url: 'baz', - } + url: 'foo', + details: { live: true }, + }, + ]; + + const textTrack1 = videoElement.addTextTrack( + captionType, + 'English', + 'en' ); - }); + const textTrack2 = videoElement.addTextTrack( + captionType, + 'Swedish', + 'se' + ); + textTrack1.groupId = 'default-text-group'; + textTrack2.groupId = 'default-text-group'; + subtitleTrackController.groupId = 'default-text-group'; - it('should trigger SUBTITLE_TRACK_SWITCH if passed -1', function () { - const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); - subtitleTrackController.trackId = 0; - subtitleTrackController.subtitleTrack = -1; + textTrack1.mode = 'disabled'; + textTrack2.mode = 'disabled'; + }); - expect(triggerSpy.firstCall).to.have.been.calledWith( - 'hlsSubtitleTrackSwitch', - { id: -1 } - ); + afterEach(function () { + hls.destroy(); + sandbox.restore(); + fake.restore(); }); - it('should trigger SUBTITLE_TRACK_LOADING if the track is live, even if it has details', function () { - const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); - subtitleTrackController.canLoad = true; - subtitleTrackController.trackId = 0; - subtitleTrackController.subtitleTrack = 2; + describe('onTextTrackChanged', function () { + it('should set subtitleTrack to -1 if disabled', function () { + expect(subtitleTrackController.subtitleTrack).to.equal(-1); - expect(triggerSpy).to.have.been.calledTwice; - expect(triggerSpy.secondCall).to.have.been.calledWith( - 'hlsSubtitleTrackLoading', - { - url: 'foo', - id: 2, - groupId: 'default-text-group', - deliveryDirectives: null, - } - ); - }); + videoElement.textTracks[0].mode = 'disabled'; + subtitleTrackController.onTextTracksChanged(); - it('should do nothing if called with out of bound indices', function () { - const clearReloadSpy = sandbox.spy(subtitleTrackController, 'clearTimer'); - subtitleTrackController.subtitleTrack = 5; - subtitleTrackController.subtitleTrack = -2; + expect(subtitleTrackController.subtitleTrack).to.equal(-1); + }); - expect(clearReloadSpy).to.have.not.been.called; - }); + it('should set subtitleTrack to 0 if hidden', function () { + expect(subtitleTrackController.subtitleTrack).to.equal(-1); - it('should do nothing if called with a non-number', function () { - subtitleTrackController.subtitleTrack = undefined; - subtitleTrackController.subtitleTrack = null; - }); + videoElement.textTracks[0].mode = 'hidden'; + subtitleTrackController.onTextTracksChanged(); - describe('toggleTrackModes', function () { - // This can be the case when setting the subtitleTrack before Hls.js attaches to the mediaElement - it('should not throw an exception if trackId is out of the mediaElement text track bounds', function () { - subtitleTrackController.trackId = 3; - subtitleTrackController.toggleTrackModes(1); + expect(subtitleTrackController.subtitleTrack).to.equal(0); }); - it('should disable all textTracks if called with -1', function () { - [].slice.call(videoElement.textTracks).forEach((t) => { - t.mode = 'showing'; - }); - subtitleTrackController.toggleTrackModes(-1); - [].slice.call(videoElement.textTracks).forEach((t) => { - expect(t.mode).to.equal('disabled'); - }); - }); + it('should set subtitleTrack to 0 if showing', function () { + expect(subtitleTrackController.subtitleTrack).to.equal(-1); - it('should not throw an exception if the mediaElement does not exist', function () { - subtitleTrackController.media = null; - subtitleTrackController.toggleTrackModes(1); + videoElement.textTracks[0].mode = 'showing'; + subtitleTrackController.onTextTracksChanged(); + + expect(subtitleTrackController.subtitleTrack).to.equal(0); }); }); - describe('onSubtitleTrackLoaded', function () { - it('exits early if the loaded track does not match the requested track', function () { - const playlistLoadedSpy = sandbox.spy( - subtitleTrackController, - 'playlistLoaded' - ); - subtitleTrackController.canLoad = true; - subtitleTrackController.trackId = 1; + describe('set subtitleTrack', function () { + it('should set active text track mode to showing', function () { + videoElement.textTracks[0].mode = 'disabled'; - const mockLoadedEvent = { - id: 999, - details: { foo: 'bar' }, - stats: new LoadStats(), - }; - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - mockLoadedEvent - ); - expect(subtitleTrackController.timer).to.equal(-1); - expect(playlistLoadedSpy).to.have.not.been.called; + subtitleTrackController.subtitleDisplay = true; + subtitleTrackController.subtitleTrack = 0; - mockLoadedEvent.id = 0; - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - mockLoadedEvent + expect(videoElement.textTracks[0].mode).to.equal('showing'); + }); + + it('should set active text track mode to hidden', function () { + videoElement.textTracks[0].mode = 'disabled'; + subtitleTrackController.subtitleDisplay = false; + subtitleTrackController.subtitleTrack = 0; + + expect(videoElement.textTracks[0].mode).to.equal('hidden'); + }); + + it('should disable previous track', function () { + // Change active track without triggering setSubtitleTrackInternal + subtitleTrackController.trackId = 0; + // Change active track and trigger setSubtitleTrackInternal + subtitleTrackController.subtitleTrack = 1; + + expect(videoElement.textTracks[0].mode).to.equal('disabled'); + }); + + it('should trigger SUBTITLE_TRACK_SWITCH', function () { + const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); + subtitleTrackController.canLoad = true; + subtitleTrackController.trackId = 0; + subtitleTrackController.subtitleTrack = 1; + + expect(triggerSpy).to.have.been.calledTwice; + expect(triggerSpy.firstCall).to.have.been.calledWith( + 'hlsSubtitleTrackSwitch', + { + id: 1, + groupId: 'default-text-group', + name: 'English', + type: 'SUBTITLES', + url: 'bar', + } ); - expect(subtitleTrackController.timer).to.equal(-1); - expect(playlistLoadedSpy).to.have.not.been.called; + }); - mockLoadedEvent.id = 1; - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - mockLoadedEvent + it('should trigger SUBTITLE_TRACK_LOADING if the track has no details', function () { + const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); + subtitleTrackController.canLoad = true; + subtitleTrackController.trackId = 0; + subtitleTrackController.subtitleTrack = 1; + + expect(triggerSpy).to.have.been.calledTwice; + expect(triggerSpy.secondCall).to.have.been.calledWith( + 'hlsSubtitleTrackLoading', + { + url: 'bar', + id: 1, + groupId: 'default-text-group', + deliveryDirectives: null, + } ); - expect(subtitleTrackController.timer).to.equal(-1); - expect(playlistLoadedSpy).to.have.been.calledOnce; }); - it('does not set the reload timer if the canLoad flag is set to false', function () { - const details = new LevelDetails(''); - subtitleTrackController.canLoad = false; + it('should not trigger SUBTITLE_TRACK_LOADING if the track has details and is not live', function () { + const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); subtitleTrackController.trackId = 1; - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - { id: 1, details, stats: new LoadStats() } + subtitleTrackController.subtitleTrack = 0; + + expect(triggerSpy).to.have.been.calledOnce; + expect(triggerSpy.firstCall).to.have.been.calledWith( + 'hlsSubtitleTrackSwitch', + { + id: 0, + groupId: 'default-text-group', + name: 'English', + type: 'SUBTITLES', + url: 'baz', + } ); - expect(subtitleTrackController.timer).to.equal(-1); }); - it('sets the live reload timer if the level is live', function () { - const details = new LevelDetails(''); - subtitleTrackController.canLoad = true; - subtitleTrackController.trackId = 1; - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - { id: 1, details, stats: new LoadStats() } + it('should trigger SUBTITLE_TRACK_SWITCH if passed -1', function () { + const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); + subtitleTrackController.trackId = 0; + subtitleTrackController.subtitleTrack = -1; + + expect(triggerSpy.firstCall).to.have.been.calledWith( + 'hlsSubtitleTrackSwitch', + { id: -1 } ); - expect(subtitleTrackController.timer).to.exist; }); - it('stops the live reload timer if the level is not live', function () { - const details = new LevelDetails(''); - details.live = false; - subtitleTrackController.trackId = 1; - subtitleTrackController.timer = self.setTimeout(() => {}, 0); - subtitleTrackController.onSubtitleTrackLoaded( - Events.SUBTITLE_TRACK_LOADED, - { id: 1, details, stats: new LoadStats() } + it('should trigger SUBTITLE_TRACK_LOADING if the track is live, even if it has details', function () { + const triggerSpy = sandbox.spy(subtitleTrackController.hls, 'trigger'); + subtitleTrackController.canLoad = true; + subtitleTrackController.trackId = 0; + subtitleTrackController.subtitleTrack = 2; + + expect(triggerSpy).to.have.been.calledTwice; + expect(triggerSpy.secondCall).to.have.been.calledWith( + 'hlsSubtitleTrackLoading', + { + url: 'foo', + id: 2, + groupId: 'default-text-group', + deliveryDirectives: null, + } ); - expect(subtitleTrackController.timer).to.equal(-1); }); - }); - describe('stopLoad', function () { - it('stops loading', function () { + it('should do nothing if called with out of bound indices', function () { const clearReloadSpy = sandbox.spy( subtitleTrackController, 'clearTimer' ); - subtitleTrackController.stopLoad(); - expect(subtitleTrackController.canLoad).to.be.false; - expect(clearReloadSpy).to.have.been.calledOnce; + subtitleTrackController.subtitleTrack = 5; + subtitleTrackController.subtitleTrack = -2; + + expect(clearReloadSpy).to.have.not.been.called; }); - }); - describe('startLoad', function () { - it('starts loading', function () { - const loadCurrentTrackSpy = sandbox.spy( - subtitleTrackController, - 'loadPlaylist' - ); - subtitleTrackController.startLoad(); - expect(subtitleTrackController.canLoad).to.be.true; - expect(loadCurrentTrackSpy).to.have.been.calledOnce; + it('should do nothing if called with a non-number', function () { + subtitleTrackController.subtitleTrack = undefined; + subtitleTrackController.subtitleTrack = null; + }); + + describe('toggleTrackModes', function () { + // This can be the case when setting the subtitleTrack before Hls.js attaches to the mediaElement + it('should not throw an exception if trackId is out of the mediaElement text track bounds', function () { + subtitleTrackController.trackId = 3; + subtitleTrackController.toggleTrackModes(1); + }); + + it('should disable all textTracks if called with -1', function () { + [].slice.call(videoElement.textTracks).forEach((t) => { + t.mode = 'showing'; + }); + subtitleTrackController.toggleTrackModes(-1); + [].slice.call(videoElement.textTracks).forEach((t) => { + expect(t.mode).to.equal('disabled'); + }); + }); + + it('should not throw an exception if the mediaElement does not exist', function () { + subtitleTrackController.media = null; + subtitleTrackController.toggleTrackModes(1); + }); + }); + + describe('onSubtitleTrackLoaded', function () { + it('exits early if the loaded track does not match the requested track', function () { + const playlistLoadedSpy = sandbox.spy( + subtitleTrackController, + 'playlistLoaded' + ); + subtitleTrackController.canLoad = true; + subtitleTrackController.trackId = 1; + + const mockLoadedEvent = { + id: 999, + details: { foo: 'bar' }, + stats: new LoadStats(), + }; + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + mockLoadedEvent + ); + expect(subtitleTrackController.timer).to.equal(-1); + expect(playlistLoadedSpy).to.have.not.been.called; + + mockLoadedEvent.id = 0; + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + mockLoadedEvent + ); + expect(subtitleTrackController.timer).to.equal(-1); + expect(playlistLoadedSpy).to.have.not.been.called; + + mockLoadedEvent.id = 1; + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + mockLoadedEvent + ); + expect(subtitleTrackController.timer).to.equal(-1); + expect(playlistLoadedSpy).to.have.been.calledOnce; + }); + + it('does not set the reload timer if the canLoad flag is set to false', function () { + const details = new LevelDetails(''); + subtitleTrackController.canLoad = false; + subtitleTrackController.trackId = 1; + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + { id: 1, details, stats: new LoadStats() } + ); + expect(subtitleTrackController.timer).to.equal(-1); + }); + + it('sets the live reload timer if the level is live', function () { + const details = new LevelDetails(''); + subtitleTrackController.canLoad = true; + subtitleTrackController.trackId = 1; + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + { id: 1, details, stats: new LoadStats() } + ); + expect(subtitleTrackController.timer).to.exist; + }); + + it('stops the live reload timer if the level is not live', function () { + const details = new LevelDetails(''); + details.live = false; + subtitleTrackController.trackId = 1; + subtitleTrackController.timer = self.setTimeout(() => {}, 0); + subtitleTrackController.onSubtitleTrackLoaded( + Events.SUBTITLE_TRACK_LOADED, + { id: 1, details, stats: new LoadStats() } + ); + expect(subtitleTrackController.timer).to.equal(-1); + }); + }); + + describe('stopLoad', function () { + it('stops loading', function () { + const clearReloadSpy = sandbox.spy( + subtitleTrackController, + 'clearTimer' + ); + subtitleTrackController.stopLoad(); + expect(subtitleTrackController.canLoad).to.be.false; + expect(clearReloadSpy).to.have.been.calledOnce; + }); + }); + + describe('startLoad', function () { + it('starts loading', function () { + const loadCurrentTrackSpy = sandbox.spy( + subtitleTrackController, + 'loadPlaylist' + ); + subtitleTrackController.startLoad(); + expect(subtitleTrackController.canLoad).to.be.true; + expect(loadCurrentTrackSpy).to.have.been.calledOnce; + }); }); }); });