Skip to content

Commit

Permalink
fix: blob urls being ignored as valid sources (#5525)
Browse files Browse the repository at this point in the history
Instead of checking for blob urls in the generic updateSourceCaches method, check for blob urls inside of handleTechSourceset before updating the source cache.

Fixes #5504.
  • Loading branch information
brandonocasey authored and gkatsev committed Oct 31, 2018
1 parent 9592a69 commit 1295d90
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 10 deletions.
29 changes: 20 additions & 9 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1366,13 +1366,6 @@ class Player extends Component {
type = srcObj.type;
}

// if we are a blob url, don't update the source cache
// blob urls can arise when playback is done via Media Source Extension (MSE)
// such as m3u8 sources with @videojs/http-streaming (VHS)
if (/^blob:/.test(src)) {
return;
}

// make sure all the caches are set to default values
// to prevent null checking
this.cache_.source = this.cache_.source || {};
Expand Down Expand Up @@ -1454,9 +1447,23 @@ class Player extends Component {
// only update the source cache when the source
// was not updated using the player api
if (!this.changingSrc_) {
let updateSourceCaches = (src) => this.updateSourceCaches_(src);
const playerSrc = this.currentSource().src;
const eventSrc = event.src;

// if we have a playerSrc that is not a blob, and a tech src that is a blob
if (playerSrc && !(/^blob:/).test(playerSrc) && (/^blob:/).test(eventSrc)) {

// if both the tech source and the player source were updated we assume
// something like @videojs/http-streaming did the sourceset and skip updating the source cache.
if (!this.lastSource_ || (this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc)) {
updateSourceCaches = () => {};
}
}

// update the source to the intial source right away
// in some cases this will be empty string
this.updateSourceCaches_(event.src);
updateSourceCaches(eventSrc);

// if the `sourceset` `src` was an empty string
// wait for a `loadstart` to update the cache to `currentSrc`.
Expand All @@ -1465,7 +1472,10 @@ class Player extends Component {
if (!event.src) {
const updateCache = (e) => {
if (e.type !== 'sourceset') {
this.updateSourceCaches_(this.techGet_('currentSrc'));
const techSrc = this.techGet('currentSrc');

this.lastSource_.tech = techSrc;
this.updateSourceCaches_(techSrc);
}

this.tech_.off(['sourceset', 'loadstart'], updateCache);
Expand All @@ -1474,6 +1484,7 @@ class Player extends Component {
this.tech_.one(['sourceset', 'loadstart'], updateCache);
}
}
this.lastSource_ = {player: this.currentSource().src, tech: event.src};

this.trigger({
src: event.src,
Expand Down
207 changes: 207 additions & 0 deletions test/unit/sourceset.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {getAbsoluteURL} from '../../src/js/utils/url.js';
const Html5 = videojs.getTech('Html5');
const wait = 1;
let qunitFn = 'module';
const blobSrc = {
src: 'blob:something',
type: 'video/mp4'
};
const testSrc = {
src: 'http://vjs.zencdn.net/v/oceans.mp4',
type: 'video/mp4'
Expand Down Expand Up @@ -141,6 +145,20 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
});

QUnit.test('data-setup one blob', function(assert) {
const done = assert.async();

this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [blobSrc]}));
this.player = videojs(this.mediaEl, {
enableSourceset: true
});

this.player.one('sourceset', (e) => {
validateSource(this.player, [blobSrc], e);
done();
});
});

QUnit.test('data-setup preload auto', function(assert) {
const done = assert.async();

Expand Down Expand Up @@ -184,6 +202,20 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
});

QUnit.test('videojs({sources: [...]}) one blob', function(assert) {
const done = assert.async();

this.player = videojs(this.mediaEl, {
enableSourceset: true,
sources: [blobSrc]
});

this.player.one('sourceset', (e) => {
validateSource(this.player, [blobSrc], e);
done();
});
});

QUnit.test('videojs({sources: [...]}) two sources', function(assert) {
const done = assert.async();

Expand Down Expand Up @@ -212,6 +244,20 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
});

QUnit.test('mediaEl.src = blob;', function(assert) {
const done = assert.async();

this.mediaEl.src = blobSrc.src;
this.player = videojs(this.mediaEl, {
enableSourceset: true
});

this.player.one('sourceset', (e) => {
validateSource(this.player, [{src: blobSrc.src, type: ''}], e);
done();
});
});

QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) {
const done = assert.async();

Expand All @@ -226,6 +272,20 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
});

QUnit.test('mediaEl.setAttribute("src", blob)', function(assert) {
const done = assert.async();

this.mediaEl.setAttribute('src', blobSrc.src);
this.player = videojs(this.mediaEl, {
enableSourceset: true
});

this.player.one('sourceset', (e) => {
validateSource(this.player, [{src: blobSrc.src, type: ''}], e);
done();
});
});

QUnit.test('<source> one source', function(assert) {
const done = assert.async();

Expand Down Expand Up @@ -352,6 +412,20 @@ QUnit[qunitFn]('sourceset', function(hooks) {
this.player.src(testSrc);
});

QUnit.test('player.src({...}) one blob', function(assert) {
const done = assert.async();

this.player = videojs(this.mediaEl, {
enableSourceset: true
});
this.player.one('sourceset', (e) => {
validateSource(this.player, [blobSrc], e);
done();
});

this.player.src(blobSrc);
});

QUnit.test('player.src({...}) preload auto', function(assert) {
const done = assert.async();

Expand Down Expand Up @@ -396,6 +470,19 @@ QUnit[qunitFn]('sourceset', function(hooks) {
this.player.tech_.el_.src = testSrc.src;
});

QUnit.test('mediaEl.src = blob', function(assert) {
const done = assert.async();

this.player = videojs(this.mediaEl, {enableSourceset: true});

this.player.one('sourceset', (e) => {
validateSource(this.player, [{src: blobSrc.src, type: ''}], e);
done();
});

this.player.tech_.el_.src = blobSrc.src;
});

QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) {
const done = assert.async();

Expand All @@ -409,6 +496,19 @@ QUnit[qunitFn]('sourceset', function(hooks) {
this.player.tech_.el_.setAttribute('src', testSrc.src);
});

QUnit.test('mediaEl.setAttribute("src", blob)"', function(assert) {
const done = assert.async();

this.player = videojs(this.mediaEl, {enableSourceset: true});

this.player.one('sourceset', (e) => {
validateSource(this.player, [{src: blobSrc.src, type: ''}], e);
done();
});

this.player.tech_.el_.setAttribute('src', blobSrc.src);
});

const appendTypes = [
{name: 'appendChild', fn: (el, obj) => el.appendChild(obj)}
];
Expand Down Expand Up @@ -685,6 +785,113 @@ QUnit[qunitFn]('sourceset', function(hooks) {
this.player.src(testSrc);
});

QUnit.test('hls -> hls -> blob -> hls', function(assert) {
this.totalSourcesets = 5;
// we have to force techFaker here as some browsers, ie edge/safari support
// native HLS.
this.player.options_.techOrder = ['techFaker'];
this.player.options_.techFaker = this.player.options_.techFaker || {};
const done = assert.async();
const m3u8One = {
src: 'http://vjs.zencdn.net/v/oceans.m3u8',
type: 'application/x-mpegURL'
};
const blobOne = 'blob:one';
const m3u8Two = {
src: 'http://vjs.zencdn.net/v/oceans-two.m3u8',
type: 'application/x-mpegURL'
};
const blobTwo = 'blob:two';
const setTechFaker = (src) => {
this.player.options_.techFaker = this.player.options_.techFaker || {};
this.player.tech_.options_ = this.player.tech_.options_ || {};
this.player.tech_.options_.sourceset = src;
this.player.options_.techFaker.sourceset = src;
};

this.player.one('sourceset', (e1) => {
validateSource(this.player, [m3u8One], e1, {event: blobOne, attr: blobOne, prop: blobOne});

this.player.one('sourceset', (e2) => {
validateSource(this.player, [m3u8Two], e2, {event: blobTwo, attr: blobTwo, prop: blobTwo});

// should change to blobSrc now
this.player.one('sourceset', (e3) => {
validateSource(this.player, [blobSrc], e3);

this.player.one('sourceset', (e4) => {
validateSource(this.player, [m3u8Two], e2, {event: blobTwo, attr: blobTwo, prop: blobTwo});

done();
});

setTechFaker(blobTwo);
this.player.src(m3u8Two);
});

setTechFaker(blobSrc.src);
this.player.src(blobSrc);
});

setTechFaker(blobTwo);
this.player.src(m3u8Two);
});

setTechFaker(blobOne);
this.player.src(m3u8One);
});

QUnit.test('hls -> mp4 -> hls -> blob', function(assert) {
this.totalSourcesets = 5;
// we have to force techFaker here as some browsers, ie edge/safari support
// native HLS.
this.player.options_.techOrder = ['techFaker'];
this.player.options_.techFaker = this.player.options_.techFaker || {};
const done = assert.async();
const m3u8One = {
src: 'http://vjs.zencdn.net/v/oceans.m3u8',
type: 'application/x-mpegURL'
};
const blobOne = 'blob:one';
const setTechFaker = (src) => {
this.player.options_.techFaker = this.player.options_.techFaker || {};
this.player.tech_.options_ = this.player.tech_.options_ || {};
this.player.tech_.options_.sourceset = src;
this.player.options_.techFaker.sourceset = src;
};

this.player.one('sourceset', (e1) => {
validateSource(this.player, [m3u8One], e1, {event: blobOne, attr: blobOne, prop: blobOne});

this.player.one('sourceset', (e2) => {
validateSource(this.player, [testSrc], e2);

// should change to blobSrc now
this.player.one('sourceset', (e3) => {
validateSource(this.player, [m3u8One], e3, {event: blobOne, attr: blobOne, prop: blobOne});

this.player.one('sourceset', (e4) => {
validateSource(this.player, [blobSrc], e4);

done();
});

setTechFaker(blobSrc.src);
this.player.src(blobSrc);
});

setTechFaker(blobOne);
this.player.src(m3u8One);
});

setTechFaker(testSrc.src);
this.player.src(testSrc);
});

setTechFaker(blobOne);
this.player.src(m3u8One);
});

QUnit.test('player.src({...}) x2 at the same time', function(assert) {
const done = assert.async();

Expand Down
14 changes: 13 additions & 1 deletion test/unit/tech/tech-faker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class TechFaker extends Tech {

constructor(options, handleReady) {
super(options, handleReady);

if (this.options_ && this.options_.sourceset) {
this.fakeSourceset();
}
if (!options || options.autoReady !== false) {
this.triggerReady();
}
Expand Down Expand Up @@ -51,7 +55,15 @@ class TechFaker extends Tech {
seeking() {
return false;
}
src() {
fakeSourceset() {
this.el_.src = this.options_.sourceset;
this.el_.setAttribute('src', this.options_.sourceset);
super.triggerSourceset(this.options_.sourceset);
}
src(src) {
if (typeof src !== 'undefined' && this.options_ && this.options_.sourceset) {
this.fakeSourceset();
}
return 'movie.mp4';
}
currentSrc() {
Expand Down

0 comments on commit 1295d90

Please sign in to comment.