Here’s a demo that streams from a live DASH server into a video texture.
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
Here’s a demo that streams from a live DASH server into a video texture.
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
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
You can see a demo of it here http://preview.punkoffice.com/hlsdemo I’m using the live Al-Jazeera stream and it runs fine on my iPhone too.
I got HLS working! https://www.babylonjs-playground.com/#9FSDC7#42
You on Rocker! : )
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
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
Thanks for sharing! It’s really useful. More information on this might be available on free account site.
Loving it
Updated playground to work with latest Babylon.js https://www.babylonjs-playground.com/#9FSDC7#49
This is fantastic! Any idea on how to get this to work on a 360-Sphere/VideoDome?
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.
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.
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'
:
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:
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()
))
});
}
}
}
@VerifiedTinker Just wondering, did you solve this issue?
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:
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();
});
}
}
}
Thanks a lot, will give a try!
The same way as with usual ‘video’ element, or you can make it with Babylon GUI.
Sorry, I didn’t. I had a specific use-case where I didn’t want any controls. Best go with labris’ proposal.