Skip to content

Commit ecdb9f4

Browse files
committed
Add a functional test for testing fast quality switches
1 parent 02d8e63 commit ecdb9f4

File tree

5 files changed

+184
-1
lines changed

5 files changed

+184
-1
lines changed

src/dash/DashHandler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ function DashHandler(config) {
235235
request.isPartialSegmentRequest = segment.isPartialSegment;
236236
request.mediaStartTime = segment.mediaStartTime;
237237
request.mediaType = getType();
238+
request.presentationStartTime = segment.presentationStartTime;
238239
request.range = segment.mediaRange;
239240
request.representation = representation;
240241
request.startTime = segment.presentationStartTime;

src/streaming/vo/FragmentRequest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class FragmentRequest {
5151
this.isPartialSegmentRequest = false;
5252
this.mediaStartTime = NaN;
5353
this.mediaType = null;
54+
this.presentationStartTime = NaN;
5455
this.range = null;
5556
this.representation = null;
5657
this.responseType = 'arraybuffer';

test/functional/adapter/DashJsAdapter.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,33 @@ class DashJsAdapter {
481481
})
482482
}
483483

484+
async replacedSegmentsInBuffer(maxNumberOfSegmentDownloadsToWait, timeoutValue, bufferInformation) {
485+
return new Promise((resolve) => {
486+
let timeout = null;
487+
488+
const _onComplete = (replaced = false) => {
489+
clearTimeout(timeout);
490+
timeout = null;
491+
this.player.off(MediaPlayer.events.FRAGMENT_LOADING_COMPLETED, _onFragmentLoadingCompleted);
492+
resolve(replaced);
493+
}
494+
const _onTimeout = () => {
495+
_onComplete(false);
496+
}
497+
const _onFragmentLoadingCompleted = (e) => {
498+
if (e && e.request && e.request.mediaType === Constants.DASH_JS.MEDIA_TYPES.VIDEO && !isNaN(e.request.presentationStartTime)) {
499+
if (e.request.presentationStartTime > bufferInformation.currentTime
500+
&& e.request.presentationStartTime < bufferInformation.currentTime + bufferInformation.forwardBuffer
501+
&& e.request.representation.id !== bufferInformation.currentRepresentation.id) {
502+
_onComplete(true);
503+
}
504+
}
505+
}
506+
timeout = setTimeout(_onTimeout, timeoutValue);
507+
this.player.on(MediaPlayer.events.FRAGMENT_LOADING_COMPLETED, _onFragmentLoadingCompleted);
508+
})
509+
}
510+
484511
async isKeepingBackwardsBufferTarget(timeoutValue, target, tolerance) {
485512
return new Promise((resolve) => {
486513
let timeout = null;

test/functional/src/Constants.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const TEST_TIMEOUT_THRESHOLDS = {
3636
TARGET_DELAY_REACHED: 20000,
3737
TARGET_BUFFER_REACHED: 20000,
3838
ENDED_EVENT_OFFSET: 2000,
39-
IS_FINISHED_OFFSET_TO_DURATION: 5000
39+
IS_FINISHED_OFFSET_TO_DURATION: 5000,
40+
FAST_SWITCH_QUALITY_VIDEO: 30000
4041
};
4142

4243
const TEST_INPUTS = {
@@ -105,6 +106,12 @@ const TEST_INPUTS = {
105106
},
106107
SWITCH_QUALITY: {
107108
TESTRUNS_PER_TYPE: 3
109+
},
110+
FAST_SWITCH_QUALITY_VIDEO: {
111+
TARGET_BUFFER: 10,
112+
TARGET_BUFFER_TOLERANCE: 10,
113+
LIVE_DELAY: 12,
114+
MAX_NUMBER_OF_SEGMENT_DOWNLOADS_TO_WAIT: 5
108115
}
109116
};
110117

@@ -192,6 +199,7 @@ TESTCASES.TEXT.FORCED_SUBTITLES = TESTCASES.CATEGORIES.TEXT + 'forced-subtitles'
192199
TESTCASES.VENDOR.GOOGLE_AD_MANAGER_EMSG = TESTCASES.CATEGORIES.VENDOR + 'google-ad-manager-emsg';
193200

194201
TESTCASES.VIDEO.SWITCH = TESTCASES.CATEGORIES.VIDEO + 'switch-video';
202+
TESTCASES.VIDEO.FAST_SWITCH_QUALITY = TESTCASES.CATEGORIES.VIDEO + 'fast-switch-quality-video';
195203

196204

197205
export default exportedObject;
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import Constants from '../../src/Constants.js';
2+
import Utils from '../../src/Utils.js';
3+
import {expect} from 'chai'
4+
import {
5+
checkIsPlaying, checkIsProgressing, checkNoCriticalErrors,
6+
initializeDashJsAdapterWithoutAttachSource, reachedTargetForwardBuffer
7+
} from '../common/common.js';
8+
9+
const TESTCASE = Constants.TESTCASES.VIDEO.FAST_SWITCH_QUALITY;
10+
let playerAdapter;
11+
12+
Utils.getTestvectorsForTestcase(TESTCASE).forEach((item) => {
13+
const mpd = item.url;
14+
let lowestVideoBitrate = NaN;
15+
let bufferInformation = {
16+
currentTime: NaN,
17+
forwardBuffer: NaN,
18+
currentRepresentation: null
19+
};
20+
21+
describe(`${TESTCASE} - ${item.name} -${mpd}`, () => {
22+
// Flag to allow skipping all subsequent tests if representations are insufficient
23+
let skipSuite = false;
24+
25+
before(() => {
26+
playerAdapter = initializeDashJsAdapterWithoutAttachSource(item);
27+
playerAdapter.updateSettings({
28+
streaming: {
29+
abr: {
30+
autoSwitchBitrate: {
31+
video: false
32+
}
33+
},
34+
delay: {
35+
liveDelay: Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.LIVE_DELAY
36+
},
37+
buffer: {
38+
bufferTimeDefault: Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.TARGET_BUFFER,
39+
bufferTimeAtTopQuality: Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.TARGET_BUFFER,
40+
bufferTimeAtTopQualityLongForm: Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.TARGET_BUFFER,
41+
},
42+
}
43+
})
44+
playerAdapter.attachSource(mpd);
45+
})
46+
47+
after(() => {
48+
playerAdapter.destroy();
49+
})
50+
51+
it(`Checking playing state`, async () => {
52+
await checkIsPlaying(playerAdapter, true)
53+
})
54+
55+
it(`Checking progressing state`, async () => {
56+
await checkIsProgressing(playerAdapter)
57+
});
58+
59+
it(`Save lowest video bitrate and reinitialize playback`, function () {
60+
const representations = playerAdapter.getRepresentationsByType(Constants.DASH_JS.MEDIA_TYPES.VIDEO);
61+
62+
if (!representations || representations.length <= 1) {
63+
skipSuite = true; // mark suite to be skipped
64+
this.skip(); // skip this test now
65+
}
66+
67+
lowestVideoBitrate = representations.reduce((lowest, rep) => {
68+
return rep.bitrateInKbit < lowest ? rep.bitrateInKbit : lowest;
69+
}, Infinity);
70+
71+
playerAdapter.updateSettings({
72+
streaming: {
73+
abr: {
74+
maxBitrate: {
75+
video: lowestVideoBitrate + 1
76+
}
77+
}
78+
}
79+
})
80+
playerAdapter.attachSource(mpd);
81+
});
82+
83+
// Subsequent tests use function() to allow access to Mocha's this and perform conditional skip
84+
it(`Checking playing state`, async function () {
85+
if (skipSuite) {
86+
this.skip();
87+
}
88+
await checkIsPlaying(playerAdapter, true)
89+
})
90+
91+
it(`Checking progressing state`, async function () {
92+
if (skipSuite) {
93+
this.skip();
94+
}
95+
await checkIsProgressing(playerAdapter)
96+
});
97+
98+
it(`Wait for forward buffer to be filled`, async function () {
99+
if (skipSuite) {
100+
this.skip();
101+
}
102+
await reachedTargetForwardBuffer(playerAdapter, Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.TARGET_BUFFER, Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.TARGET_BUFFER_TOLERANCE);
103+
});
104+
105+
it(`Set buffer information`, async function () {
106+
if (skipSuite) {
107+
this.skip();
108+
}
109+
bufferInformation.currentTime = playerAdapter.getCurrentTime();
110+
bufferInformation.forwardBuffer = playerAdapter.getBufferLengthByType(Constants.DASH_JS.MEDIA_TYPES.VIDEO);
111+
bufferInformation.currentRepresentation = playerAdapter.getCurrentRepresentationForType(Constants.DASH_JS.MEDIA_TYPES.VIDEO);
112+
});
113+
114+
it(`Enable fast ABR quality switching and expect buffer to be replaced`, async function () {
115+
if (skipSuite) {
116+
this.skip();
117+
}
118+
playerAdapter.updateSettings({
119+
streaming: {
120+
buffer: {
121+
fastSwitchEnabled: true,
122+
},
123+
abr: {
124+
autoSwitchBitrate: {
125+
video: true
126+
},
127+
maxBitrate: {
128+
video: -1
129+
}
130+
},
131+
}
132+
})
133+
const replacedBuffer = await playerAdapter.replacedSegmentsInBuffer(
134+
Constants.TEST_INPUTS.FAST_SWITCH_QUALITY_VIDEO.MAX_NUMBER_OF_SEGMENT_DOWNLOADS_TO_WAIT,
135+
Constants.TEST_TIMEOUT_THRESHOLDS.FAST_SWITCH_QUALITY_VIDEO,
136+
bufferInformation)
137+
expect(replacedBuffer).to.be.true;
138+
});
139+
it(`Expect no critical errors to be thrown`, function () {
140+
if (skipSuite) {
141+
this.skip();
142+
}
143+
checkNoCriticalErrors(playerAdapter)
144+
})
145+
})
146+
})

0 commit comments

Comments
 (0)