Youtube video texture *without* CSS3DRenderer

Not specifically babylon question but related and probably of interest to many :

Is it possible to get the MP4 stream of a Youtube video and use this as video texture in Babylon? I have seen several CSS3DRenderer based examples but like to stay away from this as I have a rather complex scene. Any suggestions?

Via Google API (need app key etc) I can get the metadata of a YT video (made a node server API endpoint for this, using the YT video ID). This provides me with a long json with several ‘formats’ and the actual mp4 stream URLs, like this one:

https://rr5---sn-gqn-vu2e7.googlevideo.com/videoplayback?expire=1667253227&ei=i

But when assigning to a video texture, I get a CORS error:

Access to video at 'https://rr5---sn-gqn-vu2e7.googlevideo.com/videoplayback?expire=1667253227&ei=i.....' from origin 'https://...my domain ..' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

However, I can open this link directly in the browser and see/hear all fine.

Does babylon have a CORS setting that I missed?

Thanks !

1 Like

Thanks but I am trying to stream a video from Youtube, so cannot treat this as an asset, unfortunately.

Maybe stream via node proxy to babylon client? Ugh

No browser (without security restrictions removed) would let you do this at the moment. The proxy server could work but you should be careful with the risk involved and the egress cost of transferring so much data :slight_smile:

1 Like

I found a way that seems to perform pretty ok. AND NO CORS ISSUES!

  1. make a node express server with this logic:
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.use('/api/ytstream', async (req, res) => {
  let url = await controller.getYoutubeStreamURL(req.query.video)
  req.pipe(request(url)).pipe(res);  
});
  1. create a controller which fetches the URL of a video by its ID:
const YT_API_KEY = '<your key>'
const YT_API_URL = 'https://youtubei.googleapis.com/youtubei/v1/player?key=' + YT_API_KEY;
const YT_API_PAYLOAD = 
`
{
  "context": {
    "client": {
     "hl": "en",
     "clientName": "WEB",
     "clientVersion": "2.20210721.00.00",
     "mainAppWebInfo": {
         "graftUrl": "/watch?v=__VIDEO_ID__"
     }
    }
   },
   "videoId": "__VIDEO_ID__"
 }  
`

exports.getYoutubeStreamURL = async (videoId) => {
  const payload = YT_API_PAYLOAD.replace(/__VIDEO_ID__/g, videoId)
  let raw = await fetch(YT_API_URL, {
    method: 'POST',
    body: payload,
  })

  let json = await raw.json();
  if(!json.streamingData) {
    return false;
  }
  // live stream has 'hlsManifestUrl' which is a *.m3u8 URL
  if(json.streamingData.hlsManifestUrl) {
    return json.streamingData.hlsManifestUrl;
  }
  // normal video: find largest resolution for now
  var maxh = 0;
  var useStream;
  json.streamingData.formats.forEach((stream) => {
    if (stream.height > maxh) {
      maxh = stream.height;
      useStream = stream;
    }
  })
  return useStream ? useStream.url : false;
}
  1. load as video texture:
    const url = ‘http://localhost:3000/api/ytstream?video=’ + videoId;
    const tex = new BABYLON.VideoTexture(“myvideo”, url , … etc)

Sweet !

TODO: how do I handle m3u8 URLs from Youtube LIVE streams ?

1 Like

You may do it with GitHub - video-dev/hls.js: HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.
There are some Playground examples with implementations.

to close this question: HLS.js works fine.just find the m3u8 stream url from youtube and create a video player with hls. all rest is same as normal video texture, incl html5 media/video events like metadataloaded, canplay, etc.

1 Like

Do we have working solution for this ?

@labris and @Jo_Mantelers answers looks like the way to go.

Some PG examples

1 Like