diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index 124de9e24d..5b8f92cd8e 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -290,7 +290,7 @@ shaka.dash.DashParser = class { let minBufferTime = 0; if (!ignoreMinBufferTime) { minBufferTime = - XmlUtils.parseAttr(mpd, 'minBufferTime', XmlUtils.parseDuration); + XmlUtils.parseAttr(mpd, 'minBufferTime', XmlUtils.parseDuration) || 0; } this.updatePeriod_ = /** @type {number} */ (XmlUtils.parseAttr( diff --git a/lib/dash/segment_template.js b/lib/dash/segment_template.js index d92d9febe6..0053799b1f 100644 --- a/lib/dash/segment_template.js +++ b/lib/dash/segment_template.js @@ -317,6 +317,8 @@ shaka.dash.SegmentTemplate = class { 'Available presentation times must be finite!'); goog.asserts.assert(availablePresentationTimes.every((x) => x >= 0), 'Available presentation times must be positive!'); + goog.asserts.assert(segmentDuration != null, + 'Segment duration must not be null!'); // In period-relative timestamps. const availablePeriodTimes = @@ -349,6 +351,9 @@ shaka.dash.SegmentTemplate = class { // These inner variables are all scoped to the inner loop, and can be used // safely in the callback below. + goog.asserts.assert(segmentDuration != null, + 'Segment duration must not be null!'); + // Relative to the period start. const positionWithinPeriod = position - startNumber; const segmentPeriodTime = positionWithinPeriod * segmentDuration; diff --git a/lib/media/segment_reference.js b/lib/media/segment_reference.js index 2fd8abf0c9..1f9d8047c1 100644 --- a/lib/media/segment_reference.js +++ b/lib/media/segment_reference.js @@ -121,7 +121,7 @@ shaka.media.SegmentReference = class { timestampOffset, appendWindowStart, appendWindowEnd) { goog.asserts.assert(startTime < endTime, 'startTime must be less than endTime'); - goog.asserts.assert((startByte < endByte) || (endByte == null), + goog.asserts.assert((endByte == null) || (startByte < endByte), 'startByte must be < endByte'); /** @type {number} */ diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index 7842d56171..6697d0871a 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -529,11 +529,12 @@ shaka.media.StreamingEngine = class { if (newSegment && !newSegmentSize) { // compute approximate segment size using stream bandwidth const duration = newSegment.getEndTime() - newSegment.getStartTime(); + const bandwidth = mediaState.stream.bandwidth || 0; // bandwidth is in bits per second, and the size is in bytes - newSegmentSize = duration * mediaState.stream.bandwidth / 8; + newSegmentSize = duration * bandwidth / 8; } - if (isNaN(newSegmentSize)) { + if (!newSegmentSize) { return false; } @@ -551,7 +552,7 @@ shaka.media.StreamingEngine = class { // If the new segment can be finished in time without risking a buffer // underflow, we should abort the old one and switch. - const bufferedAhead = bufferEnd - presentationTime; + const bufferedAhead = (bufferEnd || 0) - presentationTime; const safetyBuffer = Math.max( this.manifest_.minBufferTime || 0, this.config_.rebufferingGoal); diff --git a/lib/offline/storage.js b/lib/offline/storage.js index 3735c7f299..cc8154554c 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -1243,9 +1243,13 @@ shaka.offline.Storage = class { static forEachSegment_(stream, startTime, callback) { /** @type {?number} */ let i = stream.segmentIndex.find(startTime); - /** @type {?shaka.media.SegmentReference} */ - let ref = i == null ? null : stream.segmentIndex.get(i); + if (i == null) { + return; + } + + /** @type {?shaka.media.SegmentReference} */ + let ref = stream.segmentIndex.get(i); while (ref) { callback(ref); ref = stream.segmentIndex.get(++i); diff --git a/lib/player.js b/lib/player.js index 990e5f8141..d99897986d 100644 --- a/lib/player.js +++ b/lib/player.js @@ -1677,7 +1677,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { has.mediaElement, 'We should have a media element when loading.'); goog.asserts.assert( - wants.startTimeOfLoad != null, + !isNaN(wants.startTimeOfLoad), '|wants| should tell us when the load was originally requested'); // Since we are about to start playback, we will lock in the start time as @@ -1959,7 +1959,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { wants.uri, '|has| should have a valid uri when loading.'); goog.asserts.assert( - wants.startTimeOfLoad, + !isNaN(wants.startTimeOfLoad), '|wants| should tell us when the load was originally requested'); goog.asserts.assert( this.video_ == has.mediaElement, @@ -3425,6 +3425,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget { if (this.manifest_) { const timeline = this.manifest_.presentationTimeline; const startTime = timeline.getPresentationStartTime(); + goog.asserts.assert(startTime != null, + 'Presentation start time should not be null!'); return new Date(/* ms= */ startTime * 1000); } else if (this.video_ && this.video_.getStartDate) { // Apple's native HLS gives us getStartDate(), which is only available if @@ -4694,7 +4696,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { const bufferEnd = shaka.media.TimeRangesUtils.bufferEnd(this.video_.buffered); - if (bufferEnd >= liveEdge) { + if (bufferEnd != null && bufferEnd >= liveEdge) { return true; } } @@ -4731,7 +4733,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { // TODO: Try to remove the fudge here once we no longer manage buffering // state above the browser with playbackRate=0. const fudge = 1; // 1000 ms - return bufferEnd >= this.video_.duration - fudge; + return bufferEnd != null && bufferEnd >= this.video_.duration - fudge; } /** @@ -5005,7 +5007,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { mediaElement: null, mimeType: null, startTime: null, - startTimeOfLoad: null, + startTimeOfLoad: NaN, uri: null, }; } diff --git a/lib/routing/payload.js b/lib/routing/payload.js index 9e50e863ce..9396538e27 100644 --- a/lib/routing/payload.js +++ b/lib/routing/payload.js @@ -11,7 +11,7 @@ goog.provide('shaka.routing.Payload'); * mediaElement: HTMLMediaElement, * mimeType: ?string, * startTime: ?number, - * startTimeOfLoad: ?number, + * startTimeOfLoad: number, * uri: ?string * }} * @@ -30,11 +30,11 @@ goog.provide('shaka.routing.Payload'); * The time (in seconds) where playback should start. When |null| we will * use the content's default start time (0 for VOD and live edge for LIVE). * - * @property {?number} startTimeOfLoad + * @property {number} startTimeOfLoad * The time (in seconds) of when a load request is created. This is used to * track the latency between when the call to |Player.load| and the start * of playback. When the payload is not for a load request, this should be - * |null|. + * NaN. * * @property {?string} uri * The address of the content that will be loaded. diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 3956ca9ce0..617a7e3566 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -5,6 +5,7 @@ goog.provide('shaka.util.PlayerConfiguration'); +goog.require('goog.asserts'); goog.require('shaka.abr.SimpleAbrManager'); goog.require('shaka.util.ConfigUtils'); @@ -313,7 +314,14 @@ shaka.util.PlayerConfiguration = class { // Sort by resolution, then select all variants which match the height // of the highest SD res. There may be multiple audio bitrates for the // same video resolution. - tracksByHeight.sort((a, b) => b.height - a.height); + tracksByHeight.sort((a, b) => { + // The items in this list have already been screened for height, but the + // compiler doesn't know that. + goog.asserts.assert(a.height != null, 'Null height'); + goog.asserts.assert(b.height != null, 'Null height'); + + return b.height - a.height; + }); selectedVariants = tracksByHeight.filter((track) => { return track.height == tracksByHeight[0].height; }); diff --git a/test/media/streaming_engine_unit.js b/test/media/streaming_engine_unit.js index d3a3e11867..c90650ae38 100644 --- a/test/media/streaming_engine_unit.js +++ b/test/media/streaming_engine_unit.js @@ -1660,8 +1660,8 @@ describe('StreamingEngine', () => { mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData); // Configure with a failure callback that records the callback time. - /** @type {?number} */ - let callbackTime = null; + /** @type {number} */ + let callbackTime = 0; const failureCallback = jasmine.createSpy('failureCallback'); failureCallback.and.callFake(() => { callbackTime = Date.now(); diff --git a/test/player_integration.js b/test/player_integration.js index 739a96d98b..b12f57fb77 100644 --- a/test/player_integration.js +++ b/test/player_integration.js @@ -711,11 +711,17 @@ describe('Player', () => { function getBufferedAhead() { const end = shaka.media.TimeRangesUtils.bufferEnd(video.buffered); + if (end == null) { + return 0; + } return end - video.currentTime; } function getBufferedBehind() { const start = shaka.media.TimeRangesUtils.bufferStart(video.buffered); + if (start == null) { + return 0; + } return video.currentTime - start; } diff --git a/test/routing/walker_unit.js b/test/routing/walker_unit.js index 6be8ae44fb..ab450238c6 100644 --- a/test/routing/walker_unit.js +++ b/test/routing/walker_unit.js @@ -13,7 +13,7 @@ describe('Walker', () => { mediaElement: null, mimeType: null, startTime: null, - startTimeOfLoad: null, + startTimeOfLoad: NaN, uri: null, }; diff --git a/test/test/util/dash_parser_util.js b/test/test/util/dash_parser_util.js index 62ba074fad..44f7e26e00 100644 --- a/test/test/util/dash_parser_util.js +++ b/test/test/util/dash_parser_util.js @@ -127,10 +127,10 @@ shaka.test.Dash = class { await video.createSegmentIndex(); const position = video.segmentIndex.find(0); - expect(position).not.toBe(null); + goog.asserts.assert(position != null, 'Position should not be null!'); const reference = video.segmentIndex.get(position); - expect(reference).not.toBe(null); + goog.asserts.assert(reference != null, 'Reference should not be null!'); return reference; } diff --git a/test/test/util/manifest_generator.js b/test/test/util/manifest_generator.js index 078761c95e..ad52124ab8 100644 --- a/test/test/util/manifest_generator.js +++ b/test/test/util/manifest_generator.js @@ -586,7 +586,7 @@ shaka.test.ManifestGenerator.Stream = class { /* endTime= */ end, getUris, /* startByte= */ 0, - /* endByte= */ segmentSize, + /* endByte= */ /** @type {?number} */(segmentSize), this.initSegmentReference_, /* timestampOffset= */ 0, /* appendWindowStart= */ 0, diff --git a/third_party/closure/goog/uri/uri.js b/third_party/closure/goog/uri/uri.js index ab7ae55f84..f866611f45 100644 --- a/third_party/closure/goog/uri/uri.js +++ b/third_party/closure/goog/uri/uri.js @@ -31,6 +31,7 @@ goog.provide('goog.Uri'); goog.provide('goog.Uri.QueryData'); +goog.require('goog.asserts'); goog.require('goog.uri.utils'); goog.require('goog.uri.utils.ComponentIndex'); @@ -811,6 +812,7 @@ goog.Uri.QueryData.prototype.add = function(key, value) { this.keyMap_[key] = (values = []); } values.push(value); + goog.asserts.assert(this.count_ != null, 'Should not be null.'); this.count_++; return this; }; diff --git a/ui/resolution_selection.js b/ui/resolution_selection.js index 23dbe3f675..c87ecdab85 100644 --- a/ui/resolution_selection.js +++ b/ui/resolution_selection.js @@ -6,6 +6,7 @@ goog.provide('shaka.ui.ResolutionSelection'); +goog.require('goog.asserts'); goog.require('shaka.ui.Enums'); goog.require('shaka.ui.Locales'); goog.require('shaka.ui.Localization'); @@ -78,6 +79,11 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu { shaka.ui.Utils.setDisplay(this.button, true); tracks.sort((t1, t2) => { + // We have already screened for audio-only content, but the compiler + // doesn't know that. + goog.asserts.assert(t1.height != null, 'Null height'); + goog.asserts.assert(t2.height != null, 'Null height'); + return t2.height - t1.height; });