From b6af24f57678fb3661ca5490c1e89b2d4ebc96ad Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Mon, 8 Jun 2020 13:14:02 +0200 Subject: [PATCH] Make low latency specific reduction and multiplication factor for retry attempts and retry interval configurable. (#3279) --- src/core/Settings.js | 16 ++-- src/streaming/models/MediaPlayerModel.js | 10 ++- .../unit/streaming.models.MediaPlayerModel.js | 77 +++++++++++++++---- 3 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/core/Settings.js b/src/core/Settings.js index ae9c1107c2..82471904b8 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -87,7 +87,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * IndexSegment: 1000, * MediaSegment: 1000, * BitstreamSwitchingSegment: 1000, - * other: 1000 + * other: 1000, + * lowLatencyReductionFactor: 10 * }, * retryAttempts: { * MPD: 3, @@ -96,7 +97,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * IndexSegment: 3, * MediaSegment: 3, * BitstreamSwitchingSegment: 3, - * other: 3 + * other: 3, + * lowLatencyMultiplyFactor: 5 * }, * abr: { * movingAverageMethod: Constants.MOVING_AVERAGE_SLIDING_WINDOW, @@ -316,8 +318,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * @property {module:Settings~AudioVideoSettings} [cacheLoadThresholds={video: 50, audio: 5}] * For a given media type, the threshold which defines if the response to a fragment * request is coming from browser cache or not. - * @property {module:Settings~RequestTypeSettings} [retryIntervals] Time in milliseconds of which to reload a failed file load attempt. - * @property {module:Settings~RequestTypeSettings} [retryAttempts] Total number of retry attempts that will occur on a file load before it fails. + * @property {module:Settings~RequestTypeSettings} [retryIntervals] Time in milliseconds of which to reload a failed file load attempt. For low latency mode these values are divided by lowLatencyReductionFactor. + * @property {module:Settings~RequestTypeSettings} [retryAttempts] Total number of retry attempts that will occur on a file load before it fails. For low latency mode these values are multiplied by lowLatencyMultiplyFactor. * @property {module:Settings~AbrSettings} abr Adaptive Bitrate algorithm related settings. * @property {module:Settings~CmcdSettings} cmcd Settings related to Common Media Client Data reporting. */ @@ -406,7 +408,8 @@ function Settings() { [HTTPRequest.INIT_SEGMENT_TYPE]: 1000, [HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE]: 1000, [HTTPRequest.INDEX_SEGMENT_TYPE]: 1000, - [HTTPRequest.OTHER_TYPE]: 1000 + [HTTPRequest.OTHER_TYPE]: 1000, + lowLatencyReductionFactor: 10 }, retryAttempts: { [HTTPRequest.MPD_TYPE]: 3, @@ -415,7 +418,8 @@ function Settings() { [HTTPRequest.INIT_SEGMENT_TYPE]: 3, [HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE]: 3, [HTTPRequest.INDEX_SEGMENT_TYPE]: 3, - [HTTPRequest.OTHER_TYPE]: 3 + [HTTPRequest.OTHER_TYPE]: 3, + lowLatencyMultiplyFactor: 5 }, abr: { movingAverageMethod: Constants.MOVING_AVERAGE_SLIDING_WINDOW, diff --git a/src/streaming/models/MediaPlayerModel.js b/src/streaming/models/MediaPlayerModel.js index 37c51335cc..a5cd3f1c52 100644 --- a/src/streaming/models/MediaPlayerModel.js +++ b/src/streaming/models/MediaPlayerModel.js @@ -126,11 +126,15 @@ function MediaPlayerModel() { } function getRetryAttemptsForType(type) { - return settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.retryAttempts[type] * LOW_LATENCY_MULTIPLY_FACTOR : settings.get().streaming.retryAttempts[type]; + const lowLatencyMultiplyFactor = !isNaN(settings.get().streaming.retryAttempts.lowLatencyMultiplyFactor) ? settings.get().streaming.retryAttempts.lowLatencyMultiplyFactor : LOW_LATENCY_MULTIPLY_FACTOR; + + return settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.retryAttempts[type] * lowLatencyMultiplyFactor : settings.get().streaming.retryAttempts[type]; } function getRetryIntervalsForType(type) { - return settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.retryIntervals[type] / LOW_LATENCY_REDUCTION_FACTOR : settings.get().streaming.retryIntervals[type]; + const lowLatencyReductionFactor = !isNaN(settings.get().streaming.retryIntervals.lowLatencyReductionFactor) ? settings.get().streaming.retryIntervals.lowLatencyReductionFactor : LOW_LATENCY_REDUCTION_FACTOR; + + return settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.retryIntervals[type] / lowLatencyReductionFactor : settings.get().streaming.retryIntervals[type]; } function getLiveDelay() { @@ -221,4 +225,4 @@ function MediaPlayerModel() { //TODO see if you can move this and not export and just getter to get default value. MediaPlayerModel.__dashjs_factory_name = 'MediaPlayerModel'; -export default FactoryMaker.getSingletonFactory(MediaPlayerModel); \ No newline at end of file +export default FactoryMaker.getSingletonFactory(MediaPlayerModel); diff --git a/test/unit/streaming.models.MediaPlayerModel.js b/test/unit/streaming.models.MediaPlayerModel.js index 44a726b258..1c6d134545 100644 --- a/test/unit/streaming.models.MediaPlayerModel.js +++ b/test/unit/streaming.models.MediaPlayerModel.js @@ -12,7 +12,11 @@ const expect = chai.expect; describe('MediaPlayerModel', function () { const context = {}; const mediaPlayerModel = MediaPlayerModel(context).getInstance(); - const settings = Settings(context).getInstance(); + let settings = Settings(context).getInstance(); + + beforeEach(() => { + settings.reset(); + }); it('Method removeUTCTimingSource should throw an exception', function () { expect(mediaPlayerModel.removeUTCTimingSource.bind(mediaPlayerModel, true, 'string')).to.throw(Constants.BAD_ARGUMENT_ERROR); @@ -44,7 +48,7 @@ describe('MediaPlayerModel', function () { let FragmentLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MEDIA_SEGMENT_TYPE); expect(FragmentLoaderRetryAttempts).to.equal(3); - const s = { streaming: { retryAttempts: {}}}; + const s = {streaming: {retryAttempts: {}}}; s.streaming.retryAttempts[HTTPRequest.MEDIA_SEGMENT_TYPE] = 50; settings.update(s); @@ -53,46 +57,85 @@ describe('MediaPlayerModel', function () { }); it('should configure FragmentLoaderRetryInterval', function () { - let FragmentLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MEDIA_SEGMENT_TYPE); + let FragmentLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MEDIA_SEGMENT_TYPE); expect(FragmentLoaderRetryInterval).to.equal(1000); - const s = { streaming: { retryIntervals: {}}}; + const s = {streaming: {retryIntervals: {}}}; s.streaming.retryIntervals[HTTPRequest.MEDIA_SEGMENT_TYPE] = 50; settings.update(s); - FragmentLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MEDIA_SEGMENT_TYPE); + FragmentLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MEDIA_SEGMENT_TYPE); expect(FragmentLoaderRetryInterval).to.equal(50); }); it('should configure ManifestLoaderRetryAttempts', function () { - let ManifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); - expect(ManifestLoaderRetryAttempts).to.equal(3); + let manifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryAttempts).to.equal(3); - const s = { streaming: { retryAttempts: {}}}; + const s = {streaming: {retryAttempts: {}}}; s.streaming.retryAttempts[HTTPRequest.MPD_TYPE] = 50; settings.update(s); - ManifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); - expect(ManifestLoaderRetryAttempts).to.equal(50); + manifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryAttempts).to.equal(50); + }); + + it('should configure low latency retry attempt multiplication factor', function () { + let manifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryAttempts).to.equal(3); + + const s = { + streaming: + { + lowLatencyEnabled: true, + retryAttempts: { + lowLatencyMultiplyFactor: 10 + } + } + }; + settings.update(s); + + manifestLoaderRetryAttempts = mediaPlayerModel.getRetryAttemptsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryAttempts).to.equal(30); }); it('should configure ManifestLoaderRetryInterval', function () { - let ManifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); - expect(ManifestLoaderRetryInterval).to.equal(500); + let manifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryInterval).to.equal(500); - const s = { streaming: { retryIntervals: {}}}; + const s = {streaming: {retryIntervals: {}}}; s.streaming.retryIntervals[HTTPRequest.MPD_TYPE] = 50; settings.update(s); - ManifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); - expect(ManifestLoaderRetryInterval).to.equal(50); + manifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryInterval).to.equal(50); + }); + + it('should configure low latency retry interval reduction factor', function () { + let manifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryInterval).to.equal(500); + + const s = { + streaming: + { + lowLatencyEnabled: true, + retryIntervals: { + lowLatencyReductionFactor: 5 + } + } + }; + settings.update(s); + + manifestLoaderRetryInterval = mediaPlayerModel.getRetryIntervalsForType(HTTPRequest.MPD_TYPE); + expect(manifestLoaderRetryInterval).to.equal(100); }); it('should configure StableBufferTime', function () { - const s = { streaming: { stableBufferTime: 50 } }; + const s = {streaming: {stableBufferTime: 50}}; settings.update(s); let StableBufferTime = mediaPlayerModel.getStableBufferTime(); expect(StableBufferTime).to.equal(50); }); -}); \ No newline at end of file + +});