diff --git a/demo/config.js b/demo/config.js
index dcc9dac07d..54a4b5793c 100644
--- a/demo/config.js
+++ b/demo/config.js
@@ -449,6 +449,9 @@ shakaDemo.Config = class {
.addNumberInput_('Gap detection threshold',
'streaming.gapDetectionThreshold',
/* canBeDecimal= */ true)
+ .addNumberInput_('Gap padding',
+ 'streaming.gapPadding',
+ /* canBeDecimal= */ true)
.addNumberInput_('Gap Jump Timer Time', 'streaming.gapJumpTimerTime',
/* canBeDecimal= */ true)
.addNumberInput_('Buffering Goal', 'streaming.bufferingGoal',
diff --git a/externs/shaka/player.js b/externs/shaka/player.js
index 9601e523ad..b58bd17a9a 100644
--- a/externs/shaka/player.js
+++ b/externs/shaka/player.js
@@ -1537,6 +1537,7 @@ shaka.extern.LiveSyncConfiguration;
* alwaysStreamText: boolean,
* startAtSegmentBoundary: boolean,
* gapDetectionThreshold: number,
+ * gapPadding: number,
* gapJumpTimerTime: number,
* durationBackoff: number,
* safeSeekOffset: number,
@@ -1630,6 +1631,13 @@ shaka.extern.LiveSyncConfiguration;
* jump.
*
* Defaults to 0.5
.
+ * @property {number} gapPadding
+ * Padding added only for Xbox, Legacy Edge and Tizen.
+ * Based on our research (specific to Tizen), the gapPadding value must be
+ * greater than your GOP length.
+ * It’s crucial to verify this value according to your actual stream.
+ *
+ * Defaults to 0.01
for Xbox and Legacy Edge, Tizen at 2.
* @property {number} gapJumpTimerTime
* The polling time in seconds to check for gaps in the media.
*
diff --git a/lib/media/gap_jumping_controller.js b/lib/media/gap_jumping_controller.js
index c2267f74a6..6741d53d4e 100644
--- a/lib/media/gap_jumping_controller.js
+++ b/lib/media/gap_jumping_controller.js
@@ -223,8 +223,10 @@ shaka.media.GapJumpingController = class {
// often rounds value we want to set as currentTime and we are not able
// to jump over the gap.
if (shaka.util.Platform.isLegacyEdge() ||
- shaka.util.Platform.isXboxOne()) {
- jumpTo = Math.ceil((jumpTo + 0.01) * 100) / 100;
+ shaka.util.Platform.isXboxOne() ||
+ shaka.util.Platform.isTizen()) {
+ const gapPadding = this.config_.gapPadding;
+ jumpTo = Math.ceil((jumpTo + gapPadding) * 100) / 100;
}
const seekEnd = this.timeline_.getSeekRangeEnd();
if (jumpTo >= seekEnd) {
diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js
index 1e1bc3ebdf..eb30b2ef65 100644
--- a/lib/util/player_configuration.js
+++ b/lib/util/player_configuration.js
@@ -207,6 +207,7 @@ shaka.util.PlayerConfiguration = class {
alwaysStreamText: false,
startAtSegmentBoundary: false,
gapDetectionThreshold: 0.5,
+ gapPadding: 0.01,
gapJumpTimerTime: 0.25 /* seconds */,
durationBackoff: 1,
// Offset by 5 seconds since Chromecast takes a few seconds to start
@@ -284,6 +285,10 @@ shaka.util.PlayerConfiguration = class {
streaming.stallSkip = 0;
}
+ if (shaka.util.Platform.isTizen()) {
+ streaming.gapPadding = 2;
+ }
+
const offline = {
// We need to set this to a throw-away implementation for now as our
// default implementation will need to reference other fields in the
diff --git a/test/media/playhead_unit.js b/test/media/playhead_unit.js
index cb8dc26a6b..9472a25c3d 100644
--- a/test/media/playhead_unit.js
+++ b/test/media/playhead_unit.js
@@ -151,6 +151,18 @@ describe('Playhead', () => {
playhead.release();
});
+ function calculateGap(time) {
+ let jumpTo = time;
+ if (shaka.util.Platform.isLegacyEdge() ||
+ shaka.util.Platform.isXboxOne() ||
+ shaka.util.Platform.isTizen()) {
+ const gapPadding = shaka.util.PlayerConfiguration.createDefault()
+ .streaming.gapPadding;
+ jumpTo = Math.ceil((jumpTo + gapPadding) * 100) / 100;
+ }
+ return jumpTo;
+ }
+
function setMockDate(seconds) {
const minutes = Math.floor(seconds / 60);
seconds %= 60;
@@ -900,7 +912,7 @@ describe('Playhead', () => {
start: 5,
waitingAt: 10,
expectEvent: true,
- expectedEndTime: 11,
+ expectedEndTime: calculateGap(11),
});
playingTest('won\'t skip a buffered range', {
@@ -909,7 +921,7 @@ describe('Playhead', () => {
start: 5,
waitingAt: 10,
expectEvent: true,
- expectedEndTime: 11,
+ expectedEndTime: calculateGap(11),
});
playingTest('will jump gap into last buffer', {
@@ -918,7 +930,7 @@ describe('Playhead', () => {
start: 15,
waitingAt: 20,
expectEvent: true,
- expectedEndTime: 21,
+ expectedEndTime: calculateGap(21),
});
}); // with small gaps
@@ -928,7 +940,7 @@ describe('Playhead', () => {
start: 5,
waitingAt: 10,
expectEvent: true,
- expectedEndTime: 30,
+ expectedEndTime: calculateGap(30),
});
playingTest('will only jump one buffer', {
@@ -937,7 +949,7 @@ describe('Playhead', () => {
start: 5,
waitingAt: 10,
expectEvent: true,
- expectedEndTime: 30,
+ expectedEndTime: calculateGap(30),
});
playingTest('will jump into last buffer', {
@@ -946,7 +958,7 @@ describe('Playhead', () => {
start: 24,
waitingAt: 30,
expectEvent: true,
- expectedEndTime: 50,
+ expectedEndTime: calculateGap(50),
});
}); // with large gaps
@@ -1019,7 +1031,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 10.4,
expectEvent: true,
- expectedEndTime: 11,
+ expectedEndTime: calculateGap(11),
});
seekTest('won\'t jump multiple buffers', {
@@ -1028,7 +1040,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 10.4,
expectEvent: true,
- expectedEndTime: 11,
+ expectedEndTime: calculateGap(11),
});
seekTest('will jump into last range with seeking', {
@@ -1037,7 +1049,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 20.5,
expectEvent: true,
- expectedEndTime: 21,
+ expectedEndTime: calculateGap(21),
});
seekTest('treats large gaps as small if playhead near end', {
@@ -1045,7 +1057,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 29.2,
expectEvent: true,
- expectedEndTime: 30,
+ expectedEndTime: calculateGap(30),
});
}); // with small gaps
@@ -1055,7 +1067,7 @@ describe('Playhead', () => {
start: 5,
seekTo: 12,
expectEvent: true,
- expectedEndTime: 30,
+ expectedEndTime: calculateGap(30),
});
}); // with large gaps
}); // with buffered seeks
@@ -1080,7 +1092,7 @@ describe('Playhead', () => {
start: 4,
seekTo: 0,
expectEvent: true,
- expectedEndTime: 0.2,
+ expectedEndTime: calculateGap(0.2),
});
seekTest('will jump when seeking into gap', {
@@ -1090,7 +1102,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 30.2,
expectEvent: true,
- expectedEndTime: 31,
+ expectedEndTime: calculateGap(31),
});
seekTest('will jump when seeking to the end of a range', {
@@ -1100,7 +1112,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 30,
expectEvent: true,
- expectedEndTime: 31,
+ expectedEndTime: calculateGap(31),
});
seekTest('won\'t jump when past end', {
@@ -1141,7 +1153,7 @@ describe('Playhead', () => {
start: 24,
seekTo: 1.6,
expectEvent: true,
- expectedEndTime: 2,
+ expectedEndTime: calculateGap(2),
});
}); // with small gaps
@@ -1152,7 +1164,7 @@ describe('Playhead', () => {
start: 25,
seekTo: 0,
expectEvent: true,
- expectedEndTime: 20,
+ expectedEndTime: calculateGap(20),
});
seekTest('will jump large gaps', {
@@ -1162,7 +1174,7 @@ describe('Playhead', () => {
start: 3,
seekTo: 32,
expectEvent: true,
- expectedEndTime: 40,
+ expectedEndTime: calculateGap(40),
});
}); // with large gaps
}); // with unbuffered seeks
@@ -1237,7 +1249,7 @@ describe('Playhead', () => {
jasmine.clock().tick(500);
expect(seekCount).toBe(1);
- expect(currentTime).toBe(10);
+ expect(currentTime).toBe(calculateGap(10));
});
it('doesn\'t gap jump if paused', () => {
@@ -1287,7 +1299,7 @@ describe('Playhead', () => {
jasmine.clock().tick(500);
// There SHOULD have been a gap jump.
- expect(video.currentTime).toBe(10);
+ expect(video.currentTime).toBe(calculateGap(10));
});
// Regression test for https://github.com/shaka-project/shaka-player/issues/3451