diff --git a/src/ranges.js b/src/ranges.js index 5a9d225d8..bbccfb26c 100644 --- a/src/ranges.js +++ b/src/ranges.js @@ -162,7 +162,7 @@ export const findSoleUncommonTimeRangesEnd = function(original, update) { * @param {TimeRanges} bufferB * @return {TimeRanges} The interesection of `bufferA` with `bufferB` */ -const bufferIntersection = function(bufferA, bufferB) { +export const bufferIntersection = function(bufferA, bufferB) { let start = null; let end = null; let arity = 0; diff --git a/src/source-updater.js b/src/source-updater.js index 53d336acd..6faf2bcc7 100644 --- a/src/source-updater.js +++ b/src/source-updater.js @@ -4,7 +4,7 @@ import videojs from 'video.js'; import logger from './util/logger'; import noop from './util/noop'; -import { buffered } from './util/buffer'; +import { bufferIntersection } from './ranges.js'; import {getMimeForCodec} from '@videojs/vhs-utils/dist/codecs.js'; const updating = (type, sourceUpdater) => { @@ -322,7 +322,15 @@ export default class SourceUpdater extends videojs.EventTarget { } buffered() { - return buffered(this.videoBuffer, this.audioBuffer); + if (this.audioBuffer && !this.videoBuffer) { + return this.audioBuffered(); + } + + if (this.videoBuffer && !this.audioBuffer) { + return this.videoBuffered(); + } + + return bufferIntersection(this.audioBuffered(), this.videoBuffered()); } setDuration(duration, doneFn = noop) { diff --git a/src/util/buffer.js b/src/util/buffer.js deleted file mode 100644 index 57959f6e3..000000000 --- a/src/util/buffer.js +++ /dev/null @@ -1,85 +0,0 @@ -import videojs from 'video.js'; - -export const buffered = (videoBuffer, audioBuffer, audioDisabled) => { - let start = null; - let end = null; - let arity = 0; - const extents = []; - const ranges = []; - - // neither buffer has been created yet - if (!videoBuffer && !audioBuffer) { - return videojs.createTimeRange(); - } - - // only one buffer is configured - if (!videoBuffer) { - return audioBuffer.buffered; - } - if (!audioBuffer) { - return videoBuffer.buffered; - } - - // both buffers are configured - if (audioDisabled) { - return videoBuffer.buffered; - } - - // both buffers are empty - if (videoBuffer.buffered.length === 0 && - audioBuffer.buffered.length === 0) { - return videojs.createTimeRange(); - } - - // Handle the case where we have both buffers and create an - // intersection of the two - const videoBuffered = videoBuffer.buffered; - const audioBuffered = audioBuffer.buffered; - let count = videoBuffered.length; - - // A) Gather up all start and end times - while (count--) { - extents.push({time: videoBuffered.start(count), type: 'start'}); - extents.push({time: videoBuffered.end(count), type: 'end'}); - } - count = audioBuffered.length; - while (count--) { - extents.push({time: audioBuffered.start(count), type: 'start'}); - extents.push({time: audioBuffered.end(count), type: 'end'}); - } - // B) Sort them by time - extents.sort(function(a, b) { - return a.time - b.time; - }); - - // C) Go along one by one incrementing arity for start and decrementing - // arity for ends - for (count = 0; count < extents.length; count++) { - if (extents[count].type === 'start') { - arity++; - - // D) If arity is ever incremented to 2 we are entering an - // overlapping range - if (arity === 2) { - start = extents[count].time; - } - } else if (extents[count].type === 'end') { - arity--; - - // E) If arity is ever decremented to 1 we leaving an - // overlapping range - if (arity === 1) { - end = extents[count].time; - } - } - - // F) Record overlapping ranges - if (start !== null && end !== null) { - ranges.push([start, end]); - start = null; - end = null; - } - } - - return videojs.createTimeRanges(ranges); -}; diff --git a/test/ranges.test.js b/test/ranges.test.js index 5c279f5b0..666218321 100644 --- a/test/ranges.test.js +++ b/test/ranges.test.js @@ -496,3 +496,52 @@ QUnit.test('converts time ranges to an array', function(assert) { 'formats ranges correctly' ); }); + +QUnit.module('bufferIntersection'); + +QUnit.test('returns intersection of two buffers', function(assert) { + const a = createTimeRanges([[0, 5], [12, 100]]); + const b = createTimeRanges([[4, 5], [10, 101]]); + + assert.ok( + rangesEqual(Ranges.bufferIntersection(a, b), createTimeRanges([[4, 5], [12, 100]])), + 'returns intersection' + ); +}); + +QUnit.test('returns empty when no buffers', function(assert) { + assert.ok( + rangesEqual(Ranges.bufferIntersection(null, null), createTimeRanges()), + 'returns empty' + ); +}); + +QUnit.test('returns empty when buffers are empty', function(assert) { + const a = createTimeRanges(); + const b = createTimeRanges(); + + assert.ok( + rangesEqual(Ranges.bufferIntersection(a, b), createTimeRanges()), + 'returns empty' + ); +}); + +QUnit.test('returns empty when one buffer is empty', function(assert) { + const a = createTimeRanges([[0, 1], [2, 3]]); + const b = createTimeRanges(); + + assert.ok( + rangesEqual(Ranges.bufferIntersection(a, b), createTimeRanges()), + 'returns empty' + ); +}); + +QUnit.test('returns empty when other buffer empty', function(assert) { + const a = createTimeRanges(); + const b = createTimeRanges([[0, 1], [2, 3]]); + + assert.ok( + rangesEqual(Ranges.bufferIntersection(a, b), createTimeRanges()), + 'returns empty' + ); +}); diff --git a/test/source-updater.test.js b/test/source-updater.test.js index 6935d14a0..abe4974ac 100644 --- a/test/source-updater.test.js +++ b/test/source-updater.test.js @@ -347,6 +347,42 @@ QUnit.test('buffered returns intersection of audio and video buffers', function( this.sourceUpdater.videoBuffer = origVideoBuffer; }); +QUnit.test('buffered returns audio buffered if no video buffer', function(assert) { + const origAudioBuffer = this.sourceUpdater.audioBuffer; + + // mocking the buffered ranges in this test because it's tough to know how much each + // browser will actually buffer + this.sourceUpdater.audioBuffer = { + buffered: videojs.createTimeRanges([[1, 2], [5.5, 5.6], [10.5, 11]]) + }; + + timeRangesEqual( + this.sourceUpdater.buffered(), + this.sourceUpdater.audioBuffered(), + 'buffered is audio' + ); + + this.sourceUpdater.audioBuffer = origAudioBuffer; +}); + +QUnit.test('buffered returns video buffered if no audio buffer', function(assert) { + const origVideoBuffer = this.sourceUpdater.videoBuffer; + + // mocking the buffered ranges in this test because it's tough to know how much each + // browser will actually buffer + this.sourceUpdater.videoBuffer = { + buffered: videojs.createTimeRanges([[1.25, 1.5], [5.1, 6.1], [10.5, 10.9]]) + }; + + timeRangesEqual( + this.sourceUpdater.buffered(), + this.sourceUpdater.videoBuffered(), + 'buffered is video' + ); + + this.sourceUpdater.videoBuffer = origVideoBuffer; +}); + QUnit.test('removeAudio removes audio buffer', function(assert) { const done = assert.async(); diff --git a/test/util/buffer.test.js b/test/util/buffer.test.js deleted file mode 100644 index 5a71f8e90..000000000 --- a/test/util/buffer.test.js +++ /dev/null @@ -1,92 +0,0 @@ -import videojs from 'video.js'; -import QUnit from 'qunit'; -import { buffered } from '../../src/util/buffer'; -import {timeRangesEqual} from '../custom-assertions.js'; - -QUnit.module('buffer'); - -QUnit.test('buffered returns video buffered when no audio', function(assert) { - const videoBuffered = videojs.createTimeRanges([[0, 1], [2, 3]]); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, null, false), - videoBuffered, - 'returns video buffered' - ); -}); - -QUnit.test('buffered returns video buffered when audio disabled', function(assert) { - const videoBuffered = videojs.createTimeRanges([[0, 1], [2, 3]]); - const audioBuffered = videojs.createTimeRanges([[4, 5], [6, 7]]); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, { buffered: audioBuffered }, true), - videoBuffered, - 'returns video buffered' - ); -}); - -QUnit.test('buffered returns audio buffered when no video', function(assert) { - const audioBuffered = videojs.createTimeRanges([[4, 5], [6, 7]]); - - timeRangesEqual( - buffered(null, { buffered: audioBuffered }, false), - audioBuffered, - 'returns audio buffered' - ); -}); - -QUnit.test('buffered returns intersection of audio and video buffers', function(assert) { - const videoBuffered = videojs.createTimeRanges([[0, 5], [12, 100]]); - const audioBuffered = videojs.createTimeRanges([[4, 5], [10, 101]]); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, { buffered: audioBuffered }, false), - videojs.createTimeRanges([[4, 5], [12, 100]]), - 'returns intersection' - ); -}); - -QUnit.test('buffered returns empty when no audio or video buffers', function(assert) { - timeRangesEqual( - buffered(null, null, false), - videojs.createTimeRanges(), - 'returns empty' - ); -}); - -QUnit.test( - 'buffered returns empty when audio and video buffers are empty', - function(assert) { - const videoBuffered = videojs.createTimeRanges(); - const audioBuffered = videojs.createTimeRanges(); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, { buffered: audioBuffered }, false), - videojs.createTimeRanges(), - 'returns empty' - ); - } -); - -QUnit.test('buffered returns empty when audio buffer empty', function(assert) { - const videoBuffered = videojs.createTimeRanges([[0, 1], [2, 3]]); - const audioBuffered = videojs.createTimeRanges(); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, { buffered: audioBuffered }, false), - videojs.createTimeRanges(), - 'returns empty' - ); -}); - -QUnit.test('buffered returns empty when video buffer empty', function(assert) { - const videoBuffered = videojs.createTimeRanges(); - const audioBuffered = videojs.createTimeRanges([[0, 1], [2, 3]]); - - timeRangesEqual( - buffered({ buffered: videoBuffered }, { buffered: audioBuffered }, false), - videojs.createTimeRanges(), - 'returns empty' - ); -});