Skip to content

Commit fe28e26

Browse files
alexanderceruttijoeyparrish
authored andcommitted
fix: Content reload starttime with HLS on iOS (#4575)
A new method `shaka.media.Playhead.ready` has been added for start time operations. Fixes #4244
1 parent c514d15 commit fe28e26

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

lib/media/playhead.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
3333
* @interface
3434
*/
3535
shaka.media.Playhead = class {
36+
/**
37+
* Called when the Player is ready to begin playback. Anything that depends
38+
* on setStartTime() should be done here, not in the constructor.
39+
*
40+
* @see https://github.com/shaka-project/shaka-player/issues/4244
41+
*/
42+
ready() {}
43+
3644
/**
3745
* Set the start time. If the content has already started playback, this will
3846
* be ignored.
@@ -90,6 +98,14 @@ shaka.media.SrcEqualsPlayhead = class {
9098

9199
/** @private {shaka.util.EventManager} */
92100
this.eventManager_ = new shaka.util.EventManager();
101+
}
102+
103+
/** @override */
104+
ready() {
105+
goog.asserts.assert(
106+
this.mediaElement_ != null,
107+
'Playhead should not be released before calling ready()',
108+
);
93109

94110
// We listen for the loaded-data-event so that we know when we can
95111
// interact with |currentTime|.
@@ -102,6 +118,7 @@ shaka.media.SrcEqualsPlayhead = class {
102118
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
103119
this.started_ = true;
104120
});
121+
105122
const currentTime = this.mediaElement_.currentTime;
106123
// Using the currentTime allows using a negative number in Live HLS
107124
const newTime = Math.max(0, currentTime + this.startTime_);
@@ -239,7 +256,12 @@ shaka.media.MediaSourcePlayhead = class {
239256
/** @type {shaka.util.Timer} */
240257
this.checkWindowTimer_ = new shaka.util.Timer(() => {
241258
this.onPollWindow_();
242-
}).tickEvery(/* seconds= */ 0.25);
259+
});
260+
}
261+
262+
/** @override */
263+
ready() {
264+
this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
243265
}
244266

245267
/** @override */

lib/player.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
20552055
shaka.util.StreamUtils.variantToTrack(initialVariant));
20562056
}
20572057

2058+
this.playhead_.ready();
2059+
20582060
// Decide if text should be shown automatically.
20592061
// similar to video/audio track, we would skip switch initial text track
20602062
// if user already pick text track (via selectTextTrack api)
@@ -2392,6 +2394,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
23922394
HTMLMediaElement.HAVE_METADATA,
23932395
this.loadEventManager_,
23942396
() => {
2397+
this.playhead_.ready();
23952398
fullyLoaded.resolve();
23962399
});
23972400

test/media/playhead_unit.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ describe('Playhead', () => {
161161
Util.spyFunc(onSeek),
162162
Util.spyFunc(onEvent));
163163

164+
playhead.ready();
165+
164166
expect(video.currentTime).toBe(5);
165167
expect(playhead.getTime()).toBe(5);
166168

@@ -182,6 +184,8 @@ describe('Playhead', () => {
182184
Util.spyFunc(onSeek),
183185
Util.spyFunc(onEvent));
184186

187+
playhead.ready();
188+
185189
expect(video.addEventListener).toHaveBeenCalledWith(
186190
'loadedmetadata', jasmine.any(Function), jasmine.anything());
187191
expect(video.addEventListener).not.toHaveBeenCalledWith(
@@ -223,6 +227,8 @@ describe('Playhead', () => {
223227
Util.spyFunc(onSeek),
224228
Util.spyFunc(onEvent));
225229

230+
playhead.ready();
231+
226232
video.on['seeking']();
227233
expect(playhead.getTime()).toBe(5);
228234
expect(video.currentTime).toBe(5);
@@ -264,6 +270,8 @@ describe('Playhead', () => {
264270
Util.spyFunc(onSeek),
265271
Util.spyFunc(onEvent));
266272

273+
playhead.ready();
274+
267275
expect(playhead.getTime()).toBe(59); // duration - durationBackoff
268276
expect(video.currentTime).toBe(59); // duration - durationBackoff
269277
});
@@ -302,6 +310,8 @@ describe('Playhead', () => {
302310
Util.spyFunc(onSeek),
303311
Util.spyFunc(onEvent));
304312

313+
playhead.ready();
314+
305315
expect(playhead.getTime()).toBe(30);
306316
});
307317

@@ -314,6 +324,8 @@ describe('Playhead', () => {
314324
Util.spyFunc(onSeek),
315325
Util.spyFunc(onEvent));
316326

327+
playhead.ready();
328+
317329
expect(video.addEventListener).toHaveBeenCalledWith(
318330
'loadedmetadata', jasmine.any(Function), jasmine.anything());
319331

@@ -347,6 +359,8 @@ describe('Playhead', () => {
347359
Util.spyFunc(onSeek),
348360
Util.spyFunc(onEvent));
349361

362+
playhead.ready();
363+
350364
expect(video.addEventListener).toHaveBeenCalledWith(
351365
'loadedmetadata', jasmine.any(Function), jasmine.anything());
352366

@@ -380,6 +394,8 @@ describe('Playhead', () => {
380394
Util.spyFunc(onSeek),
381395
Util.spyFunc(onEvent));
382396

397+
playhead.ready();
398+
383399
// This has to periodically increment the mock date to allow the onSeeking_
384400
// handler to seek, if appropriate.
385401

@@ -652,6 +668,8 @@ describe('Playhead', () => {
652668
expect(currentTime).toBe(1000);
653669
seekCount = 0;
654670

671+
playhead.ready();
672+
655673
// The availability window slips ahead.
656674
timeline.getSeekRangeStart.and.returnValue(1030);
657675
timeline.getSeekRangeEnd.and.returnValue(1030);
@@ -696,6 +714,8 @@ describe('Playhead', () => {
696714
Util.spyFunc(onSeek),
697715
Util.spyFunc(onEvent));
698716

717+
playhead.ready();
718+
699719
video.on['seeking']();
700720
expect(video.currentTime).toBe(5);
701721
expect(playhead.getTime()).toBe(5);
@@ -728,6 +748,8 @@ describe('Playhead', () => {
728748
Util.spyFunc(onSeek),
729749
Util.spyFunc(onEvent));
730750

751+
playhead.ready();
752+
731753
video.on['seeking']();
732754
expect(video.currentTime).toBe(5);
733755
expect(playhead.getTime()).toBe(5);
@@ -765,6 +787,9 @@ describe('Playhead', () => {
765787

766788
video.currentTime = 0;
767789
video.seeking = true;
790+
791+
playhead.ready();
792+
768793
// "video.seeking" stays true until the buffered range intersects with
769794
// "video.currentTime". Playhead should correct anyway.
770795
video.on['seeking']();
@@ -809,6 +834,8 @@ describe('Playhead', () => {
809834
Util.spyFunc(onSeek),
810835
Util.spyFunc(onEvent));
811836

837+
playhead.ready();
838+
812839
/**
813840
* Prevent retries on the initial start time seek. This will ensure that
814841
* only one call is made for the initial seek, and that any additional calls
@@ -933,6 +960,8 @@ describe('Playhead', () => {
933960
Util.spyFunc(onSeek),
934961
Util.spyFunc(onEvent));
935962

963+
playhead.ready();
964+
936965
jasmine.clock().tick(500);
937966
for (let time = data.start; time < data.waitingAt; time++) {
938967
// We don't want to run tick() for 1 second because it will trigger
@@ -1142,6 +1171,8 @@ describe('Playhead', () => {
11421171
Util.spyFunc(onSeek),
11431172
Util.spyFunc(onEvent));
11441173

1174+
playhead.ready();
1175+
11451176
jasmine.clock().tick(500);
11461177
expect(onEvent).not.toHaveBeenCalled();
11471178

@@ -1190,6 +1221,8 @@ describe('Playhead', () => {
11901221
Util.spyFunc(onSeek),
11911222
Util.spyFunc(onEvent));
11921223

1224+
playhead.ready();
1225+
11931226
playhead.notifyOfBufferingChange();
11941227
jasmine.clock().tick(500);
11951228

@@ -1212,6 +1245,8 @@ describe('Playhead', () => {
12121245
Util.spyFunc(onSeek),
12131246
Util.spyFunc(onEvent));
12141247

1248+
playhead.ready();
1249+
12151250
playhead.notifyOfBufferingChange();
12161251
jasmine.clock().tick(500);
12171252

@@ -1236,6 +1271,8 @@ describe('Playhead', () => {
12361271
Util.spyFunc(onSeek),
12371272
Util.spyFunc(onEvent));
12381273

1274+
playhead.ready();
1275+
12391276
playhead.notifyOfBufferingChange();
12401277
jasmine.clock().tick(500);
12411278

@@ -1260,6 +1297,8 @@ describe('Playhead', () => {
12601297
Util.spyFunc(onSeek),
12611298
Util.spyFunc(onEvent));
12621299

1300+
playhead.ready();
1301+
12631302
playhead.notifyOfBufferingChange();
12641303
jasmine.clock().tick(500);
12651304

@@ -1287,6 +1326,8 @@ describe('Playhead', () => {
12871326
Util.spyFunc(onSeek),
12881327
Util.spyFunc(onEvent));
12891328

1329+
playhead.ready();
1330+
12901331
jasmine.clock().tick(500);
12911332
expect(onEvent).not.toHaveBeenCalled();
12921333

test/test/util/simple_fakes.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ shaka.test.FakePlayhead = class {
339339
/** @private {number} */
340340
this.stallsDetected_ = 0;
341341

342+
/** @type {!jasmine.Spy} */
343+
this.ready = jasmine.createSpy('ready');
344+
342345
/** @type {!jasmine.Spy} */
343346
this.setStartTime = jasmine.createSpy('setStartTime')
344347
.and.callFake((value) => {

0 commit comments

Comments
 (0)