# Live streaming video texture

**URL:** <https://forum.babylonjs.com/t/live-streaming-video-texture/6746>\
**Category:** Demos and projects\
**Created:** [November 2, 2019, 5:12am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746 "2019-11-02T05:12:47Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![ozRocker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ozrocker/32/186_2.png) [@ozRocker](https://forum.babylonjs.com/u/ozRocker)\
**Post date:** [November 2, 2019, 5:12am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/1 "2019-11-02T05:12:47Z")

</div>

Here’s a demo that streams from a live DASH server into a video texture.

[https://www.babylonjs-playground.com/#9FSDC7#13](https://www.babylonjs-playground.com/#9FSDC7#13)

I just found some German streams. I don’t know what they’re saying but hopefully its nice things

---

<div class="post-metadata">

**Author:** ![ozRocker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ozrocker/32/186_2.png) [@ozRocker](https://forum.babylonjs.com/u/ozRocker)\
**Post date:** [November 2, 2019, 9:06am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/3 "2019-11-02T09:06:35Z")

</div>

DASH streaming doesn’t work on iOS devices so I made a demo using the HLS protocol as well.

I can’t get the playground to work. I’m having problems accessing functions from external scripts. The playground is here [https://www.babylonjs-playground.com/#9FSDC7#28](https://www.babylonjs-playground.com/#9FSDC7#28)

You can see a demo of it here [http://preview.punkoffice.com/hlsdemo](http://preview.punkoffice.com/hlsdemo) I’m using the live Al-Jazeera stream and it runs fine on my iPhone too.

---

<div class="post-metadata">

**Author:** ![ozRocker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ozrocker/32/186_2.png) [@ozRocker](https://forum.babylonjs.com/u/ozRocker)\
**Post date:** [November 23, 2019, 1:30am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/4 "2019-11-23T01:30:39Z")

</div>

I got HLS working! [https://www.babylonjs-playground.com/#9FSDC7#42](https://www.babylonjs-playground.com/#9FSDC7#42)

---

<div class="post-metadata">

**Author:** ![aFalcon](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/afalcon/32/7015_2.png) [@aFalcon](https://forum.babylonjs.com/u/aFalcon)\
**Post date:** [November 23, 2019, 3:51am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/5 "2019-11-23T03:51:39Z")

</div>

You on 🔥 Rocker! : )

---

<div class="post-metadata">

**Author:** ![ozRocker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ozrocker/32/186_2.png) [@ozRocker](https://forum.babylonjs.com/u/ozRocker)\
**Post date:** [November 24, 2019, 12:55am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/6 "2019-11-24T00:55:06Z")

</div>

HLS files have .m3u8 extension. There’s a whole bunch of them which are publicly available, like on this list here: [https://pastebin.com/HqNd5tU8](https://pastebin.com/HqNd5tU8)

There are some restrictions though. If the website is HTTPS then you can only play HTTPS streams. Also some streams cannot be accessed by outside players. Periscope streams are HLS but if you try to access them outside of the Periscope website/app then it’ll say unauthorised access.

Best way to test if the stream is still up is to add an HLS player extension to your Chrome browser, then you can type .m3u8 files in the browser and it’ll play it right there, however, this will work even with private streams like Periscope

---

<div class="post-metadata">

**Author:** ![flashhmob](https://avatars.discourse-cdn.com/v4/letter/f/ad7895/32.png) [@flashhmob](https://forum.babylonjs.com/u/flashhmob)\
**Post date:** [November 29, 2019, 2:04pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/7 "2019-11-29T14:04:11Z")

</div>

Thanks for sharing! It’s really useful. More information on this might be available on free account site.

---

<div class="post-metadata">

**Author:** ![sebavan](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/sebavan/32/57_2.png) [@sebavan](https://forum.babylonjs.com/u/sebavan)\
**Post date:** [November 29, 2019, 2:08pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/8 "2019-11-29T14:08:45Z")

</div>

Loving it 🙂

---

<div class="post-metadata">

**Author:** ![ozRocker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ozrocker/32/186_2.png) [@ozRocker](https://forum.babylonjs.com/u/ozRocker)\
**Post date:** [April 16, 2020, 1:48am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/9 "2020-04-16T01:48:57Z")

</div>

Updated playground to work with latest Babylon.js [https://www.babylonjs-playground.com/#9FSDC7#49](https://www.babylonjs-playground.com/#9FSDC7#49)

---

<div class="post-metadata">

**Author:** ![Tilade](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/tilade/32/10268_2.png) [@Tilade](https://forum.babylonjs.com/u/Tilade)\
**Post date:** [July 29, 2020, 11:06pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/10 "2020-07-29T23:06:23Z")

</div>

This is fantastic! Any idea on how to get this to work on a 360-Sphere/VideoDome?

---

<div class="post-metadata">

**Author:** ![Tilade](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/tilade/32/10268_2.png) [@Tilade](https://forum.babylonjs.com/u/Tilade)\
**Post date:** [August 6, 2020, 12:29pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/11 "2020-08-06T12:29:41Z")

</div>

For the curious – I got it working for 360 video. Had to use a simple sphere instead of a VideoDome object. Otherwise, all else is the same.

---

<div class="post-metadata">

**Author:** ![keverw](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/keverw/32/7769_2.png) [@keverw](https://forum.babylonjs.com/u/keverw)\
**Post date:** [August 19, 2020, 8:17am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/12 "2020-08-19T08:17:35Z")

</div>

Pretty Neat! I’m guessing since you use the `<video>` tag maybe a way to add some JS to separate the audio feed? So then you could do positional audio, maybe even setup multiple "speakers’? Sorta like in real life people have setups with multiple speakers.

Also I was kinda wonder what would happen if you played a 3D Movie in BJS. I seen an app on the Oculus Quest that lets you rent 3D movies like the Titanic. Watched the trailer and was pretty impressed.

---

<div class="post-metadata">

**Author:** ![VerifiedTinker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/verifiedtinker/32/20439_2.png) [@VerifiedTinker](https://forum.babylonjs.com/u/VerifiedTinker)\
**Post date:** [December 5, 2021, 12:19pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/13 "2021-12-05T12:19:58Z")

</div>

Thank you! Sorry for reviving an old thread, but do you have any idea how this might work with TypeScript? I basically did the same thing, but it complains that it `Cannot find name 'Hls'`:

```auto
if (Hls.isSupported()) { // Here, and on the line below: "Cannot find name 'Hls'."
            const hls = new Hls();
            hls.loadSource(video.src);
            hls.attachMedia(video);

            hls.on(Hls.Events.MANIFEST_PARSED, () => {
                tv.actionManager.registerAction(new BABYLON.ExecuteCodeAction(
                    BABYLON.ActionManager.OnPickTrigger, 
                    (_event) => video.play()
                ))
            });
        }

```

The entire code:

```auto
class Playground {
    public static CreateScene(engine: BABYLON.Engine, canvas: HTMLCanvasElement): BABYLON.Scene {
        const scene = new BABYLON.Scene(engine);
        const camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene);
        camera.setTarget(BABYLON.Vector3.Zero());
        camera.attachControl(canvas, true);

        const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = 0.7;

        const streamUrl = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8";
        const hlsjs = Playground.ImportHLS();
        const video = Playground.CreateVideo(streamUrl);
        const videoTexture = Playground.CreateVideoTexture(video, scene);
        const tvMaterial = Playground.CreateTVMaterial(videoTexture, scene);
        const tv = Playground.CreateTV(2, scene);

        tv.material = tvMaterial;
        tv.actionManager = new BABYLON.ActionManager(scene);
        hlsjs.onload = () => Playground.InitializeHLS(video, tv);

        return scene;
    }

    private static CreateVideoTexture(video: HTMLVideoElement,scene: BABYLON.Scene): BABYLON.VideoTexture {
        return new BABYLON.VideoTexture("HLS TV Texture", video, scene, false);
    }

    private static CreateTVMaterial(texture: BABYLON.VideoTexture, scene: BABYLON.Scene): BABYLON.StandardMaterial {
        const tvMaterial = new BABYLON.StandardMaterial("TVMaterial", scene);
        tvMaterial.backFaceCulling = false;
        tvMaterial.diffuseTexture = texture;
        tvMaterial.emissiveColor = BABYLON.Color3.White();
        return tvMaterial;
    }

    private static ImportHLS(): HTMLScriptElement {
        const script = document.createElement("script");
        script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
        document.head.appendChild(script);
        return script;
    }

    private static CreateTV(height: number, scene: BABYLON.Scene): BABYLON.Mesh {
        const aspectRatio = 16 / 9;
        const tv = BABYLON.MeshBuilder.CreatePlane("TV", {
            width: height * aspectRatio,
            height: height
        }, scene);
        tv.rotate(BABYLON.Axis.Y, Math.PI, BABYLON.Space.WORLD);
        return tv;
    }

    private static CreateVideo(streamUrl: string): HTMLVideoElement {
        const video = document.createElement("video");
        video.playsInline = true;
        video.autoplay = false;
        video.muted = false;
        video.crossOrigin = "anonymous";
        video.loop = true;
        video.src = streamUrl;

        video.style.width = '1px';
        video.style.height = '1px';
        video.style.position = 'absolute';
        video.style.opacity = '0';
        video.style.zIndex = '-1000';
        video.style.pointerEvents = 'none';

        document.body.appendChild(video);

        return video;
    }

    private static InitializeHLS(video: HTMLVideoElement, tv: BABYLON.Mesh) {
        if (Hls.isSupported()) {
            const hls = new Hls();
            hls.loadSource(video.src);
            hls.attachMedia(video);

            hls.on(Hls.Events.MANIFEST_PARSED, () => {
                tv.actionManager.registerAction(new BABYLON.ExecuteCodeAction(
                    BABYLON.ActionManager.OnPickTrigger, 
                    (_event) => video.play()
                ))
            });
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![labris](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/labris/32/12576_2.png) [@labris](https://forum.babylonjs.com/u/labris)\
**Post date:** [May 23, 2022, 12:42am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/14 "2022-05-23T00:42:44Z")

</div>

@VerifiedTinker Just wondering, did you solve this issue?

---

<div class="post-metadata">

**Author:** ![VerifiedTinker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/verifiedtinker/32/20439_2.png) [@VerifiedTinker](https://forum.babylonjs.com/u/VerifiedTinker)\
**Post date:** [May 23, 2022, 4:53am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/15 "2022-05-23T04:53:39Z")

</div>

Not in the Playground. However, it works fine in the actual app, where I installed Hls.js via NPM. If that’s what you’re looking for, here’s the code for it:

```auto
import {
  Axis,
  Color3,
  Mesh,
  MeshBuilder,
  Scene,
  Space,
  StandardMaterial,
  VideoTexture,
  VideoTextureSettings,
} from "@babylonjs/core";
import "@babylonjs/core/Meshes/meshBuilder";
import Hls from "hls.js";

export class Livestream {
  public static CreateScreen(
    screenHeight: number,
    streamUrl: string,
    scene: Scene
  ): Mesh {
    const videoElement = Livestream.CreateVideoElement(streamUrl);
    const videoTexture = Livestream.CreateVideoTexture(videoElement, scene);
    const screenMaterial = Livestream.CreateScreenMaterial(videoTexture, scene);
    const screen = Livestream.CreateScreenMesh(screenHeight, scene);

    screen.material = screenMaterial;
    Livestream.InitializeHls(videoElement, scene);
    return screen;
  }

  private static CreateVideoElement(url: string): HTMLVideoElement {
    const video = document.createElement("video");
    video.playsInline = true;
    video.autoplay = false;
    video.muted = false;
    video.crossOrigin = "anonymous";
    video.loop = true;
    video.src = url;

    video.style.width = "1px";
    video.style.height = "1px";
    video.style.position = "absolute";
    video.style.opacity = "0";
    video.style.zIndex = "-1000";
    video.style.pointerEvents = "none";

    document.body.appendChild(video);

    return video;
  }

  private static CreateVideoTexture(
    videoElement: HTMLVideoElement,
    scene: Scene
  ): VideoTexture {
    const settings: VideoTextureSettings = {
      autoUpdateTexture: true,
      autoPlay: false,
      muted: false,
      loop: true,
    };

    return new VideoTexture(
      "HLS_screen_texture",
      videoElement,
      scene,
      false,
      false,
      undefined,
      settings
    );
  }

  private static CreateScreenMaterial(
    videoTexture: VideoTexture,
    scene: Scene
  ): StandardMaterial {
    const tvMaterial = new StandardMaterial("TVMaterial", scene);
    tvMaterial.backFaceCulling = false;
    tvMaterial.diffuseTexture = videoTexture;
    tvMaterial.emissiveColor = Color3.White();
    return tvMaterial;
  }

  private static CreateScreenMesh(screenHeight: number, scene: Scene): Mesh {
    const aspectRatio = 16 / 9;
    const screen = MeshBuilder.CreatePlane(
      "livestream_screen",
      {
        width: screenHeight * aspectRatio,
        height: screenHeight,
      },
      scene
    );

    screen.rotate(Axis.Y, Math.PI, Space.WORLD);
    return screen;
  }

  private static InitializeHls(
    videoElement: HTMLVideoElement,
    scene: Scene
  ): void {
    if (Hls.isSupported()) {
      const hls = new Hls();
      hls.loadSource(videoElement.src);
      hls.attachMedia(videoElement);

      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        scene.onPointerDown = () => videoElement.play();
      });
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![labris](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/labris/32/12576_2.png) [@labris](https://forum.babylonjs.com/u/labris)\
**Post date:** [May 23, 2022, 11:20am UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/16 "2022-05-23T11:20:44Z")

</div>

Thanks a lot, will give a try!

---

<div class="post-metadata">

**Author:** ![labris](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/labris/32/12576_2.png) [@labris](https://forum.babylonjs.com/u/labris)\
**Post date:** [June 1, 2022, 5:23pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/18 "2022-06-01T17:23:24Z")

</div>

> [@Buddha123](#):
>
> Is there any way, I can place Video Controls like Timeline, Mute Button and all over the video where we are playing video as a texture ?

The [same way](https://www.google.com/search?q=custom+video+controls+codepen&oq=custom+video+controls&aqs=chrome.2.69i57j0i512l4j0i22i30l5.6018j0j7&sourceid=chrome&ie=UTF-8) as with usual ‘video’ element, or you can make it with Babylon GUI.

---

<div class="post-metadata">

**Author:** ![VerifiedTinker](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/verifiedtinker/32/20439_2.png) [@VerifiedTinker](https://forum.babylonjs.com/u/VerifiedTinker)\
**Post date:** [June 1, 2022, 6:35pm UTC](https://forum.babylonjs.com/t/live-streaming-video-texture/6746/19 "2022-06-01T18:35:15Z")

</div>

Sorry, I didn’t. I had a specific use-case where I didn’t want any controls. Best go with labris’ proposal.
