diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 230fa8f1a0..111c9eb9cd 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -570,7 +570,9 @@ shakaExtern.AbrConfiguration; * abr: shakaExtern.AbrConfiguration, * preferredAudioLanguage: string, * preferredTextLanguage: string, - * restrictions: shakaExtern.Restrictions + * restrictions: shakaExtern.Restrictions, + * playRangeStart: number, + * playRangeEnd: number * }} * * @property {shakaExtern.DrmConfiguration} drm @@ -593,6 +595,12 @@ shakaExtern.AbrConfiguration; * @property {shakaExtern.Restrictions} restrictions * The application restrictions to apply to the tracks. The track must * meet all the restrictions to be playable. + * @property {number} playRangeStart + * Optional playback and seek start time in seconds. Defaults to 0 if + * not provided. + * @property {number} playRangeEnd + * Optional playback and seek end time in seconds. Defaults to the end of + * the presentation if not provided. * @exportDoc */ shakaExtern.PlayerConfiguration; diff --git a/lib/media/presentation_timeline.js b/lib/media/presentation_timeline.js index a52f7780cc..19c1e9ac35 100644 --- a/lib/media/presentation_timeline.js +++ b/lib/media/presentation_timeline.js @@ -60,6 +60,9 @@ shaka.media.PresentationTimeline = function( /** @private {boolean} */ this.static_ = true; + + /** @private {number} */ + this.segmentAvailabilityStart_ = 0; }; @@ -248,11 +251,23 @@ shaka.media.PresentationTimeline.prototype.getSegmentAvailabilityStart = shaka.media.PresentationTimeline.prototype.getSafeAvailabilityStart = function(offset) { if (this.segmentAvailabilityDuration_ == Infinity) - return 0; + return this.segmentAvailabilityStart_; var end = this.getSegmentAvailabilityEnd(); var start = Math.min(end - this.segmentAvailabilityDuration_ + offset, end); - return Math.max(0, start); + return Math.max(this.segmentAvailabilityStart_, start); +}; + + +/** + * Sets the presentation's current segment availability start time. + * + * @param {number} time + * @export + */ +shaka.media.PresentationTimeline.prototype.setAvailabilityStart = + function(time) { + this.segmentAvailabilityStart_ = time; }; diff --git a/lib/player.js b/lib/player.js index 229417fd46..a824e5df5b 100644 --- a/lib/player.js +++ b/lib/player.js @@ -544,6 +544,31 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime, this.currentAudioLanguage_ = this.config_.preferredAudioLanguage; this.currentTextLanguage_ = this.config_.preferredTextLanguage; + var fullDuration = this.manifest_.presentationTimeline.getDuration(); + var playRangeEnd = this.config_.playRangeEnd; + var playRangeStart = this.config_.playRangeStart; + + if (playRangeStart > 0) { + if (this.isLive()) { + shaka.log.warning('PlayerConfiguration.playRangeStart has been ' + + 'configured for live content. Ignoring the setting.'); + } else { + this.manifest_.presentationTimeline.setAvailabilityStart( + playRangeStart); + } + } + + // If the playback has been configured to end before the end of the + // presentation, update the duration unless it's live content. + if (playRangeEnd < fullDuration) { + if (this.isLive()) { + shaka.log.warning('PlayerConfiguration.playRangeEnd has been ' + + 'configured for live content. Ignoring the setting.'); + } else { + this.manifest_.presentationTimeline.setDuration(playRangeEnd); + } + } + // Wait for MediaSource to open before continuing. return Promise.all([ this.drmEngine_.attach(this.video_), @@ -554,7 +579,8 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime, // MediaSource is open, so create the Playhead, MediaSourceEngine, and // StreamingEngine. - this.playhead_ = this.createPlayhead(opt_startTime); + var startTime = opt_startTime || this.config_.playRangeStart; + this.playhead_ = this.createPlayhead(startTime); this.playheadObserver_ = this.createPlayheadObserver(); this.mediaSourceEngine_ = this.createMediaSourceEngine(); @@ -1761,7 +1787,9 @@ shaka.Player.prototype.defaultConfig_ = function() { maxPixels: Infinity, minBandwidth: 0, maxBandwidth: Infinity - } + }, + playRangeStart: 0, + playRangeEnd: Infinity }; }; diff --git a/test/player_unit.js b/test/player_unit.js index e83726cc78..d2aec36e7b 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -665,6 +665,28 @@ describe('Player', function() { } }); }); + + it('configures play and seek range for VOD', function(done) { + player.configure({playRangeStart: 5, playRangeEnd: 10}); + var timeline = new shaka.media.PresentationTimeline(300, 0); + timeline.setStatic(true); + manifest = new shaka.test.ManifestGenerator() + .setTimeline(timeline) + .addPeriod(0) + .addVariant(0) + .addVideo(1) + .build(); + goog.asserts.assert(manifest, 'manifest must be non-null'); + var parser = new shaka.test.FakeManifestParser(manifest); + var factory = function() { return parser; }; + player.load('', 0, factory).then(function() { + var seekRange = player.seekRange(); + expect(seekRange.start).toBe(5); + expect(seekRange.end).toBe(10); + }) + .catch(fail) + .then(done); + }); }); describe('AbrManager', function() {