I'm new to A-Frame. i try to make a demo which i put a-video in the scene. it work on my computer but got a black plane in my mobile device. here is the screenshot and code.
demo run in Macos chrome & safari
demo run in ios safari, it work on chrome.
<html>
<head>
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script id="vertexShader" type="glsl">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="glsl">
uniform sampler2D tex;
uniform float texWidth;
uniform float texHeight;
uniform vec3 keyColor;
uniform float similarity;
uniform float smoothness;
uniform float spill;
varying vec2 vUv;
// From https://github.com/libretro/glsl-shaders/blob/master/nnedi3/shaders/rgb-to-yuv.glsl
vec2 RGBtoUV(vec3 rgb) {
return vec2(
rgb.r * -0.169 + rgb.g * -0.331 + rgb.b * 0.5 + 0.5,
rgb.r * 0.5 + rgb.g * -0.419 + rgb.b * -0.081 + 0.5
);
}
vec4 ProcessChromaKey(vec2 texCoord) {
vec4 rgba = texture2D(tex, texCoord);
float chromaDist = distance(RGBtoUV(texture2D(tex, texCoord).rgb), RGBtoUV(keyColor));
float baseMask = chromaDist - similarity;
float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);
rgba.a = fullMask;
float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);
float desat = clamp(rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722, 0., 1.);
rgba.rgb = mix(vec3(desat, desat, desat), rgba.rgb, spillVal);
return rgba;
}
void main(void) {
vec2 texCoord = vUv;
gl_FragColor = ProcessChromaKey(texCoord);
}
</script>
<script>
AFRAME.registerShader('custom-shader', {
init: function(data) {
const video = document.getElementById('video')
const texture = new THREE.VideoTexture(video)
this.material = new THREE.ShaderMaterial( {
uniforms: {
tex: { value: texture },
keyColor: {
value: new THREE.Color(0x00ff00)
},
texWidth: {
value: 1920
},
texHeight: {
value: 1080
},
similarity: {
value: 0.4
},
smoothness: {
value: 0.08
},
spill: {
value: 0.1
}
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
});
}
});
</script>
</head>
<body>
<a-scene>
<a-assets>
<video id="video" autoplay loop="true" muted="true" crossOrigin="anonymous">
<source src="./assets/mixkit-a-woman-talking-on-the-phone-on-a-green-screen-24388-medium.mp4" type='video/mp4'>
</video>
</a-assets>
<a-video material="shader: custom-shader;" webkit-playsinline playsinline position="0 0 -10" height="9" width="16" rotation="0 0 0"></a-video>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
<script>
const video = document.getElementById('video');
video.play();
</script>
</body>
</html>
and this is my demo
i have set some attrs to a-video like, webkit-playsinline playsinline. but it doesn't work.
is anyone know how to fix it? thanks.
I believe that mobile browsers require an explicit "user interaction" such as a "click" to play a video. This is required because many mobile devices use cellular data and if I embed a 1GB video on my website and autoplay it in the background it will drain your battery and you will not be happy with the data charges. Try adding a click handler to start the video and if clicking the "Start" button works then you are running into this limitation. The documentation also hints at this
Mobile and desktop browsers have been tightening the video autoplay policies to preserve battery and avoid intrusive advertisements. Most browsers now require a user action (such as a click or tap event) to start video playback:
and they even provide an example
<html>
<head>
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script id="vertexShader" type="glsl">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="glsl">
uniform sampler2D tex;
uniform float texWidth;
uniform float texHeight;
uniform vec3 keyColor;
uniform float similarity;
uniform float smoothness;
uniform float spill;
varying vec2 vUv;
// From https://github.com/libretro/glsl-shaders/blob/master/nnedi3/shaders/rgb-to-yuv.glsl
vec2 RGBtoUV(vec3 rgb) {
return vec2(
rgb.r * -0.169 + rgb.g * -0.331 + rgb.b * 0.5 + 0.5,
rgb.r * 0.5 + rgb.g * -0.419 + rgb.b * -0.081 + 0.5
);
}
vec4 ProcessChromaKey(vec2 texCoord) {
vec4 rgba = texture2D(tex, texCoord);
float chromaDist = distance(RGBtoUV(texture2D(tex, texCoord).rgb), RGBtoUV(keyColor));
float baseMask = chromaDist - similarity;
float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);
rgba.a = fullMask;
float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);
float desat = clamp(rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722, 0., 1.);
rgba.rgb = mix(vec3(desat, desat, desat), rgba.rgb, spillVal);
return rgba;
}
void main(void) {
vec2 texCoord = vUv;
gl_FragColor = ProcessChromaKey(texCoord);
}
</script>
<script>
AFRAME.registerShader('custom-shader', {
init: function(data) {
const video = document.getElementById('video')
const texture = new THREE.VideoTexture(video)
this.material = new THREE.ShaderMaterial( {
uniforms: {
tex: { value: texture },
keyColor: {
value: new THREE.Color(0x00ff00)
},
texWidth: {
value: 1920
},
texHeight: {
value: 1080
},
similarity: {
value: 0.4
},
smoothness: {
value: 0.08
},
spill: {
value: 0.1
}
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
});
}
});
</script>
</head>
<body>
<a-scene>
<a-assets>
<video id="video" autoplay loop="true" muted="true" crossOrigin="anonymous">
<source src="./assets/mixkit-a-woman-talking-on-the-phone-on-a-green-screen-24388-medium.mp4" type='video/mp4'>
</video>
</a-assets>
<a-video material="shader: custom-shader;" webkit-playsinline playsinline position="0 0 -10" height="9" width="16" rotation="0 0 0"></a-video>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
<button id="start">Start</button>
<script>
const start = document.getElementById('start');
const video = document.getElementById('video');
start.addEventListener('click', () => {
video.play();
});
</script>
</body>
</html>