diff --git a/lib/player.js b/lib/player.js index b18118bdf5..e668782ca3 100644 --- a/lib/player.js +++ b/lib/player.js @@ -5010,7 +5010,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget { } await stream.createSegmentIndex(); - const ref = stream.segmentIndex[Symbol.iterator]().seek(time); + const iter = stream.segmentIndex.getIteratorForTime(time); + const ref = iter ? iter.next().value : null; if (!ref) { return null; } diff --git a/test/player_unit.js b/test/player_unit.js index a719eb784d..be81b5fb75 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -124,7 +124,13 @@ describe('Player', () => { player.createDrmEngine = () => drmEngine; player.createNetworkingEngine = () => networkingEngine; - player.createPlayhead = () => playhead; + player.createPlayhead = (startTime) => { + const callableSetStartTime = + shaka.test.Util.spyFunc(playhead.setStartTime); + callableSetStartTime(startTime); + playhead.setStartTime.calls.reset(); + return playhead; + }; player.createMediaSourceEngine = () => mediaSourceEngine; player.createStreamingEngine = () => streamingEngine; } @@ -3710,6 +3716,51 @@ describe('Player', () => { }); }); + describe('config streaming.startAtSegmentBoundary', () => { + // Test for https://github.com/shaka-project/shaka-player/issues/4188 + // In that issue, using streaming.startAtSegmentBoundary in v4.0.0 caused + // an exception due to the use of a removed method. + it('adjusts start time', async () => { + player.configure('streaming.startAtSegmentBoundary', true); + + // In order to adjust the start time to a segment boundary, we need + // segment descriptions in the segment index. So create a non-default + // fake manifest. + const timeline = new shaka.media.PresentationTimeline(300, 0); + timeline.setStatic(true); + // This duration is used by useSegmentTemplate below to decide how many + // references to generate. + timeline.setDuration(300); + manifest = shaka.test.ManifestGenerator.generate((manifest) => { + manifest.presentationTimeline = timeline; + manifest.addVariant(0, (variant) => { + variant.addVideo(1, (stream) => { + stream.useSegmentTemplate( + '$Number$.mp4', /* segmentDuration= */ 10); + }); + }); + }); + + await player.load(fakeManifestUri, 12, fakeMimeType); + + expect(playhead.setStartTime).toHaveBeenCalledWith(10); + }); + + it('does not fail with no segments', async () => { + player.configure('streaming.startAtSegmentBoundary', true); + + // Without useSegmentTemplate in the fake manifest, the call to + // getIteratorForTime() in Player produces a null iterator. Using the + // default fake manifest should cover that case. But just to make sure, + // verify that we have no segments before calling load(). + const variant = manifest.variants[0]; + expect(variant.video.segmentIndex.getIteratorForTime(0)).toBeNull(); + expect(variant.audio.segmentIndex.getIteratorForTime(0)).toBeNull(); + + await player.load(fakeManifestUri, 0, fakeMimeType); + }); + }); + /** * Gets the currently active variant track. * @return {shaka.extern.Track} diff --git a/test/test/util/simple_fakes.js b/test/test/util/simple_fakes.js index 93ac04b33b..4cfb33de05 100644 --- a/test/test/util/simple_fakes.js +++ b/test/test/util/simple_fakes.js @@ -308,11 +308,18 @@ shaka.test.FakePlayhead = class { /** @type {!jasmine.Spy} */ this.setRebufferingGoal = jasmine.createSpy('setRebufferingGoal'); + /** @private {number} */ + this.startTime_ = 0; + /** @type {!jasmine.Spy} */ - this.setStartTime = jasmine.createSpy('setStartTime'); + this.setStartTime = jasmine.createSpy('setStartTime') + .and.callFake((value) => { + this.startTime_ = value; + }); /** @type {!jasmine.Spy} */ - this.getTime = jasmine.createSpy('getTime').and.returnValue(0); + this.getTime = jasmine.createSpy('getTime') + .and.callFake(() => this.startTime_); /** @type {!jasmine.Spy} */ this.setBuffering = jasmine.createSpy('setBuffering');