From 41305dc62e4caa2df85a5ad142202d39b3e351c2 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Tue, 4 Dec 2018 11:15:54 -0800 Subject: [PATCH] Regression test for language/MediaSource failure This is fairly tricky to reproduce. First, we must trigger the auto-display of subtitles. We didn't have any tests that covered this before, so we needed this anyway. This is triggered by careful choice of content language and language preferences, which required us to add language information to our simulated test content. I also added tests for the cases where we should *not* trigger auto-display of subtitles. Second, the setup phase for text must complete more quickly than the setup for audio & video. In real life, this happens when text is non-segmented VTT and audio & video use DASH's SegmentBase. In the test, this is accomplished by delaying createSegmentIndex() of audio and video in our simulated content. This will cause auto-display of subtitles to trigger a setup race in StreamingEngine. When text wins the race, MediaSource errors follow for audio and video. Issue #1696 Change-Id: I1c22089925486da642368bec269a55d8556900d1 --- test/player_integration.js | 82 ++++++++++++++++++++++++++++ test/test/util/manifest_generator.js | 12 ++++ test/test/util/test_scheme.js | 55 +++++++++++++++++++ 3 files changed, 149 insertions(+) diff --git a/test/player_integration.js b/test/player_integration.js index bfb1c60170..3d012d2324 100644 --- a/test/player_integration.js +++ b/test/player_integration.js @@ -239,6 +239,88 @@ describe('Player', function() { expect(textTrack.cues).not.toBe(null); } }); + + it('is called automatically if language prefs match', async () => { + // If the text is a match for the user's preferences, and audio differs + // from text, we enable text display automatically. + + // NOTE: This is also a regression test for #1696, in which a change + // to this feature broke StreamingEngine initialization. + + const preferredTextLanguage = 'fa'; // The same as in the content itself + player.configure({preferredTextLanguage: preferredTextLanguage}); + + // Now load a version of Sintel with delayed setup of video & audio + // streams and wait for completion. + await player.load('test:sintel_realistic_compiled'); + // By this point, a MediaSource error would be thrown in a repro of bug + // #1696. + + // Make sure the automatic setting took effect. + expect(player.isTextTrackVisible()).toBe(true); + + // Make sure the content we tested with has text tracks, that the config + // we used matches the text language, and that the audio language differs. + // These will catch any changes to the underlying content that would + // invalidate the test setup. + expect(player.getTextTracks().length).not.toBe(0); + const textTrack = player.getTextTracks()[0]; + expect(textTrack.language).toEqual(preferredTextLanguage); + + const variantTrack = player.getVariantTracks()[0]; + expect(variantTrack.language).not.toEqual(textTrack.language); + }); + + it('is not called automatically without language pref match', async () => { + // If the text preference doesn't match the content, we do not enable text + // display automatically. + + const preferredTextLanguage = 'xx'; // Differs from the content itself + player.configure({preferredTextLanguage: preferredTextLanguage}); + + // Now load the content and wait for completion. + await player.load('test:sintel_realistic_compiled'); + + // Make sure the automatic setting did not happen. + expect(player.isTextTrackVisible()).toBe(false); + + // Make sure the content we tested with has text tracks, that the config + // we used does not match the text language, and that the text and audio + // languages do not match each other (to keep this distinct from the next + // test case). This will catch any changes to the underlying content that + // would invalidate the test setup. + expect(player.getTextTracks().length).not.toBe(0); + const textTrack = player.getTextTracks()[0]; + expect(textTrack.language).not.toEqual(preferredTextLanguage); + + const variantTrack = player.getVariantTracks()[0]; + expect(variantTrack.language).not.toEqual(textTrack.language); + }); + + it('is not called automatically with audio and text match', async () => { + // If the audio and text tracks use the same language, we do not enable + // text display automatically, no matter the text preference. + + const preferredTextLanguage = 'und'; // The same as in the content itself + player.configure({preferredTextLanguage: preferredTextLanguage}); + + // Now load the content and wait for completion. + await player.load('test:sintel_compiled'); + + // Make sure the automatic setting did not happen. + expect(player.isTextTrackVisible()).toBe(false); + + // Make sure the content we tested with has text tracks, that the + // config we used matches the content, and that the text and audio + // languages match each other. This will catch any changes to the + // underlying content that would invalidate the test setup. + expect(player.getTextTracks().length).not.toBe(0); + const textTrack = player.getTextTracks()[0]; + expect(textTrack.language).toEqual(preferredTextLanguage); + + const variantTrack = player.getVariantTracks()[0]; + expect(variantTrack.language).toEqual(textTrack.language); + }); }); describe('plays', function() { diff --git a/test/test/util/manifest_generator.js b/test/test/util/manifest_generator.js index 76b018fff5..27d3fb8721 100644 --- a/test/test/util/manifest_generator.js +++ b/test/test/util/manifest_generator.js @@ -623,6 +623,18 @@ shaka.test.ManifestGenerator.prototype.textStream = function(uri) { }; +/** + * Force a delay in createSegmentIndex to delay setup. This can be useful in + * certain tests. + * + * @return {!shaka.test.ManifestGenerator} + */ +shaka.test.ManifestGenerator.prototype.delayCreateSegmentIndex = function() { + this.currentStream_().createSegmentIndex = () => shaka.test.Util.delay(1); + return this; +}; + + /** * Converts the init segment of the current stream into jasmine.any. * diff --git a/test/test/util/test_scheme.js b/test/test/util/test_scheme.js index 58cdfbaf05..78f6e72709 100644 --- a/test/test/util/test_scheme.js +++ b/test/test/util/test_scheme.js @@ -118,7 +118,43 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + + // Like 'sintel' above, but with languages and delayed setup. + // These extra features help expose some edge cases. + 'sintel_realistic': { + video: { + initSegmentUri: '/base/test/test/assets/sintel-video-init.mp4', + mvhdOffset: 0x24, + segmentUri: '/base/test/test/assets/sintel-video-segment.mp4', + tfdtOffset: 0x38, + segmentDuration: 10, + presentationTimeOffset: 0, + mimeType: 'video/mp4', + codecs: 'avc1.42c01e', + delaySetup: true, // Necessary to repro #1696 + }, + audio: { + initSegmentUri: '/base/test/test/assets/sintel-audio-init.mp4', + mvhdOffset: 0x20, + segmentUri: '/base/test/test/assets/sintel-audio-segment.mp4', + tfdtOffset: 0x3c, + segmentDuration: 10.005, + presentationTimeOffset: 0, + mimeType: 'audio/mp4', + codecs: 'mp4a.40.2', + language: 'uk', // Necessary to repro #1696 + delaySetup: true, // Necessary to repro #1696 + }, + text: { + uri: '/base/test/test/assets/text-clip.vtt', + mimeType: 'text/vtt', + language: 'fa', // Necessary to repro #1696 + }, + duration: 30, + }, + // 'sintel_short_periods' : Generated by createManifests(). + 'sintel_no_text': { video: { initSegmentUri: '/base/test/test/assets/sintel-video-init.mp4', @@ -142,6 +178,7 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + 'sintel-enc': { video: { initSegmentUri: '/base/test/test/assets/encrypted-sintel-video-init.mp4', @@ -180,6 +217,7 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + 'multidrm': { video: { initSegmentUri: '/base/test/test/assets/multidrm-video-init.mp4', @@ -227,6 +265,7 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + 'multidrm_no_init_data': { video: { initSegmentUri: '/base/test/test/assets/multidrm-video-init.mp4', @@ -264,6 +303,7 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + 'cea-708_ts': { video: { segmentUri: '/base/test/test/assets/captions-test.ts', @@ -275,6 +315,7 @@ shaka.test.TestScheme.DATA = { }, duration: 30, }, + 'cea-708_mp4': { video: { initSegmentUri: '/base/test/test/assets/cea-init.mp4', @@ -346,6 +387,8 @@ shaka.test.TestScheme.createManifests = function(shaka, suffix) { } /** + * Not for simulating non-segmented text streams. + * * @param {shaka.test.ManifestGenerator} manifestGenerator * @param {Object} data * @param {shaka.util.ManifestParserUtils.ContentType} contentType @@ -361,6 +404,14 @@ shaka.test.TestScheme.createManifests = function(shaka, suffix) { data[contentType].segmentDuration) .closedCaptions(data[contentType].closedCaptions); + if (data[contentType].language) { + manifestGenerator.language(data[contentType].language); + } + + if (data[contentType].delaySetup) { + manifestGenerator.delayCreateSegmentIndex(); + } + if (data.licenseServers) { for (let keySystem in data.licenseServers) { manifestGenerator.addDrmInfo(keySystem) @@ -411,6 +462,10 @@ shaka.test.TestScheme.createManifests = function(shaka, suffix) { gen.addTextStream(3) .mime(data.text.mimeType, data.text.codecs) .textStream(absoluteUri.toString()); + + if (data.text.language) { + gen.language(data.text.language); + } } MANIFESTS[name + suffix] = gen.build();