three.jsm3u8360-virtual-reality

m3u8 360 virtual reality video with Three js


Is there a way to play 360 video with Three js coming from m3u8 file?

I found a similar question here but no answer: https://github.com/mrdoob/three.js/issues/8216

https://threejs.org/examples/webgl_video_panorama_equirectangular.html

I have used the code from thee website for playing 360 video and this work fine when normal mp4 video url is used but when I try to include m3u8 then it fails with error.MEDIA_ERR_SRC_NOT_SUPPORTED:

  The video could not be loaded, either because the server or network failed or because the format is not supported.

Here is the code:

<video id="video" width=960 height=540 style="display:none">
  <source src="https://bitmovin.com/player-content/playhouse-vr/m3u8s/105560.m3u8" type="application/x-mpegURL">
</video>



        var camera, scene, renderer;

        var isUserInteracting = false,
            lon = 0, lat = 0,
            phi = 0, theta = 0,
            distance = 50,
            onPointerDownPointerX = 0,
            onPointerDownPointerY = 0,
            onPointerDownLon = 0,
            onPointerDownLat = 0;

        init();
        animate();

        function init() {

            var container, mesh;

            container = document.getElementById( 'container' );

            camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
            camera.target = new THREE.Vector3( 0, 0, 0 );

            scene = new THREE.Scene();

            var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
            // invert the geometry on the x-axis so that all of the faces point inward
            geometry.scale( - 1, 1, 1 );

            var video = document.getElementById( 'video' );
            video.play();

            var texture = new THREE.VideoTexture( video );
            var material = new THREE.MeshBasicMaterial( { map: texture } );

            mesh = new THREE.Mesh( geometry, material );

            scene.add( mesh );

            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );

            document.addEventListener( 'mousedown', onDocumentMouseDown, false );
            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            document.addEventListener( 'mouseup', onDocumentMouseUp, false );
            document.addEventListener( 'wheel', onDocumentMouseWheel, false );

            //

            window.addEventListener( 'resize', onWindowResize, false );

        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize( window.innerWidth, window.innerHeight );

        }

        function onDocumentMouseDown( event ) {

            event.preventDefault();

            isUserInteracting = true;

            onPointerDownPointerX = event.clientX;
            onPointerDownPointerY = event.clientY;

            onPointerDownLon = lon;
            onPointerDownLat = lat;

        }

        function onDocumentMouseMove( event ) {

            if ( isUserInteracting === true ) {

                lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
                lat = ( onPointerDownPointerY - event.clientY ) * 0.1 + onPointerDownLat;

            }

        }

        function onDocumentMouseUp() {

            isUserInteracting = false;

        }

        function onDocumentMouseWheel( event ) {

            distance += event.deltaY * 0.05;

            distance = THREE.MathUtils.clamp( distance, 1, 50 );

        }

        function animate() {

            requestAnimationFrame( animate );
            update();

        }

        function update() {

            lat = Math.max( - 85, Math.min( 85, lat ) );
            phi = THREE.MathUtils.degToRad( 90 - lat );
            theta = THREE.MathUtils.degToRad( lon );

            camera.position.x = distance * Math.sin( phi ) * Math.cos( theta );
            camera.position.y = distance * Math.cos( phi );
            camera.position.z = distance * Math.sin( phi ) * Math.sin( theta );

            camera.lookAt( camera.target );

            renderer.render( scene, camera );

        }

    

m3u8 plays on chrome (and some other browsers). I have been using it for years. The problem is in combining m3u8 format with three js

m3u8 works as can be seen here: https://hls-js.netlify.app/demo/ enter this url : https://bitmovin.com/player-content/playhouse-vr/m3u8s/105560.m3u8


Solution

  • HLS (.m3u8 file type) and MPEG DASH (.mpd file type) are adaptive bit rate streaming protocols (ABR).

    ABR creates multiple bit rate versions of your content and chunks them, so the client can choose the next chunk from the best bit rate for the device and current network conditions (https://stackoverflow.com/a/42365034/334402).

    To play a HSL or DASH file you typically use a Javascript based video player, which in turn will leverage the HTML5 Media Soure Extension API - HTML5 MSE:

    The Javascript video player downloads the chunks of video, choosing the most appropriate bit rate for each chunk, and assembles them and passes them to the HTML5 video player.

    Many Javascript Video Players will support 360 video and you may find it is easier to simply integrate one of these into your project. As an example, extensions to the commonly used videoJS player will support 360 video: