I noticed a few older posts which were similar, so this is a bit of a public service announcement after figuring out a solution.
My desire was to play a small video within a preloader while my application was loading. I started with the video example code. On Safari I ran into the issue of autoplay not working without user input of some sort. At first I thought I had a solution by muting the netstream: netStream.soundTransform = new SoundTransform(0);
This got it working on desktop Safari but I later discovered the mobile Safari still wasn’t working. So I ended up with the following hack to make it work.
<video>
html elements are allowed to autoplay, so my first step was to add that to the html and hide it under my application window. I also added a script that ensured it was loaded before my main application started.
<!--this went in my html template:-->
<video id="introVideo" autoplay loop muted playsinline>
<source src="./assets/preloader.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
//also in my html template:
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
const video = document.getElementById('introVideo');
function setupApp() {
lime.embed (
"::APP_FILE::",
"content",
::WIN_WIDTH::,
::WIN_HEIGHT::
);
}
// Check if already loaded
if (document.readyState === 'complete') {
setupApp();
} else {
window.addEventListener('load', setupApp);
}
});
</script>
With that in place, my custom preloader required a way to steal the frames from the video.
private function init() {
var videoBitmap = new Bitmap(grabVideoFrame());
videoBitmap.width = 190;
videoBitmap.height = 190;
videoBitmap.y = 95;
videoBitmap.x = 122;
addChild(videoBitmap);
this.addEventListener(Event.ENTER_FRAME, updateVideoBitmap);
}
private function grabVideoFrame():BitmapData{
var cwVideoRef:VideoElement = cast Browser.document.getElementById('introVideo');
var renderer:OpenGLRenderer = cast @:privateAccess Lib.current.stage.__renderer;
var context:Context3D = @:privateAccess renderer.__context3D;
var gl = @:privateAccess context.__context.webgl;
if (context == null) {
trace("Error getting WebGL context");
return null;
}
gl = @:privateAccess context.gl;
var internalFormat = gl.RGBA;
var format = gl.RGBA;
var __texture:RectangleTexture = context.createRectangleTexture(cwVideoRef.videoWidth, cwVideoRef.videoHeight, BGRA, false);
@:privateAccess context.__bindGLTexture2D(__texture.__textureID);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, gl.UNSIGNED_BYTE, cwVideoRef);
var bmd = BitmapData.fromTexture(__texture);
return bmd;
}
private function updateVideoBitmap(e:Event) {
try{
videoBitmap.bitmapData = grabVideoFrame();
} catch(e:Dynamic) {
trace("Error updating video bitmap: " + e);
}
}
Note: In the “onComplete” function of the preloader I also put in lines to remove the ENTER_FRAME event listener, then remove the video from the html once I no longer needed it. Also note that this solution is relying on underlying methods in the webgl context so it should not work when using other contexts.
In the end, Safari on iPhone plays the video as expected, within the videoBitmap I created. I hope this helps somebody!