From 7b8cebaff8669650ccd801146e7a4abb68abcee5 Mon Sep 17 00:00:00 2001 From: machenmusik Date: Sat, 13 May 2017 12:40:42 -0400 Subject: [PATCH 1/8] change isHLS to directly accept src and type; move shader substitution to materialtextureloaded to make it testable --- src/systems/material.js | 2 +- src/utils/material.js | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/systems/material.js b/src/systems/material.js index ec7a3829cbf..66255f08473 100755 --- a/src/systems/material.js +++ b/src/systems/material.js @@ -146,7 +146,7 @@ module.exports.System = registerSystem('material', { setTextureProperties(texture, data); // if we're on iOS, and the video is HLS, we currently need to do some hacks - if (utils.device.isIOS() && isHLS(videoEl)) { + if (utils.device.isIOS() && isHLS(videoEl.getAttribute('src'), videoEl.getAttribute('type'))) { // really it's BGRA, so this needs correction in shader texture.format = THREE.RGBAFormat; texture.needsCorrectionBGRA = true; diff --git a/src/utils/material.js b/src/utils/material.js index c6e6bca42fe..941da1fba8f 100644 --- a/src/utils/material.js +++ b/src/utils/material.js @@ -1,5 +1,7 @@ var THREE = require('../lib/three'); +var HLS_MIMETYPES = ['application/x-mpegurl', 'application/vnd.apple.mpegurl']; + /** * Update `material` texture property (usually but not always `map`) * from `data` property (usually but not always `src`) @@ -118,11 +120,17 @@ function handleTextureEvents (el, texture) { // Video events. if (!texture.image || texture.image.tagName !== 'VIDEO') { return; } + + // With Travis CI, the actual videos are never loaded, + // so for the iOS HLS shader adaptation to be testable, + // it needs to be done on materialtextureloaded not materialvideoloadeddata! + + // Check to see if we need to use iOS 10 HLS shader. + if (texture.needsCorrectionBGRA && texture.needsCorrectionFlipY) { + el.setAttribute('material', 'shader', 'ios10hls'); + } + texture.image.addEventListener('loadeddata', function emitVideoTextureLoadedDataAll () { - // Check to see if we need to use iOS 10 HLS shader. - if (texture.needsCorrectionBGRA && texture.needsCorrectionFlipY) { - el.setAttribute('material', 'shader', 'ios10hls'); - } el.emit('materialvideoloadeddata', {src: texture.image, texture: texture}); }); texture.image.addEventListener('ended', function emitVideoTextureEndedAll () { @@ -132,8 +140,14 @@ function handleTextureEvents (el, texture) { } module.exports.handleTextureEvents = handleTextureEvents; -module.exports.isHLS = function (videoEl) { - if (videoEl.type && videoEl.type.toLowerCase() in ['application/x-mpegurl', 'application/vnd.apple.mpegurl']) { return true; } - if (videoEl.src && videoEl.src.toLowerCase().indexOf('.m3u8') > 0) { return true; } +/** + * Given video element src and type, guess whether stream is HLS. + * + * @param {string} src - src from video element (generally URL to content). + * @param {string} type - type from video element (generally MIME type if present). + */ +module.exports.isHLS = function (src, type) { + if (type && HLS_MIMETYPES.includes(type.toLowerCase())) { return true; } + if (src && src.toLowerCase().indexOf('.m3u8') > 0) { return true; } return false; }; From 84b8894dad2d951dca63481842ed06f077e52e44 Mon Sep 17 00:00:00 2001 From: machenmusik Date: Sat, 13 May 2017 12:41:22 -0400 Subject: [PATCH 2/8] add tests for iOS HLS functionality --- tests/core/shader.test.js | 55 ++++++++++++++++++++++++++++++++++ tests/systems/material.test.js | 36 +++++++++++++++++++++- tests/utils/isHLS.test.js | 39 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tests/utils/isHLS.test.js diff --git a/tests/core/shader.test.js b/tests/core/shader.test.js index 83ad656eb0b..85fa74af620 100644 --- a/tests/core/shader.test.js +++ b/tests/core/shader.test.js @@ -152,6 +152,61 @@ suite('shader', function () { instance.attributes['src'])); }); + test('iOS HLS video uses appropriate shader', function (done) { + var shader = this.shader; + var el = this.el; + var initSpy = this.sinon.spy(shader.prototype, 'init'); + var updateSpy = this.sinon.spy(shader.prototype, 'update'); + assert.notOk(initSpy.called); + assert.notOk(updateSpy.called); + + // Mock iOS. NOTE: this doesn't work... el.sceneEl.isIOS = true; + var realIsIOS = AFRAME.utils.device.isIOS; + AFRAME.utils.device.isIOS = function () { return true; }; + assert.equal(AFRAME.utils.device.isIOS(), true); + + // Set up and verify video element to be treated as HLS. + var videoEl = document.createElement('video'); + videoEl.setAttribute('src', VIDEO); + videoEl.setAttribute('type', 'application/x-mpegurl'); + assert.equal(AFRAME.utils.material.isHLS(videoEl.getAttribute('src'), videoEl.getAttribute('type')), true); + + // With Travis CI, the actual videos are never loaded, + // so check for materialtextureloaded not materialvideoloadeddata, + // and don't try to assert the uniform values + el.addEventListener('materialtextureloaded', function () { + var material = el.components.material; + if (!material) { return; } + var instance = material.shader; + assert.equal(instance.material['_texture_src'].image.getAttribute('src'), VIDEO); + + // Verify system thought this was iOS HLS. + assert.equal(AFRAME.utils.device.isIOS(), true); + assert.equal(AFRAME.utils.material.isHLS(videoEl.getAttribute('src'), videoEl.getAttribute('type')), true); + + // Wait for other handlers to fire. + setTimeout(function () { + // Undo mock of iOS. + AFRAME.utils.device.isIOS = realIsIOS; + + // Verify shader was substituted. + assert.equal(material.data.shader, 'ios10hls'); + + done(); + }); + }); + el.setAttribute('material', {shader: 'testShader', src: videoEl}); + var material = el.components.material; + var instance = material.shader; + assert.ok(instance); + assert.ok(initSpy.calledOnce); + assert.ok(updateSpy.calledOnce); + // The value won't be assigned until the texture loads. + assert.ok(instance.uniforms['src']); + assert.notOk(instance.attributes && (instance.attributes['map'] || + instance.attributes['src'])); + }); + test('otherMap loads inline video', function (done) { var shader = this.shader; var el = this.el; diff --git a/tests/systems/material.test.js b/tests/systems/material.test.js index 42379766413..53c5a7ebc72 100644 --- a/tests/systems/material.test.js +++ b/tests/systems/material.test.js @@ -1,4 +1,4 @@ -/* global assert, process, setup, suite, test */ +/* global assert, process, setup, suite, test, AFRAME, THREE */ var entityFactory = require('../helpers').entityFactory; var IMAGE1 = 'base/tests/assets/test.png'; @@ -167,6 +167,40 @@ suite('material system', function () { }); }); + test('sets texture flags appropriately when given a