Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I stream a video from a ServiceWorker?

I have a service worker that catches my page's requests (fetch event), and when the URL matches a certain pattern, it would fetch another URL and replace the response with that new content. It works perfectly for text data (JS, XML...) or binary data (e.g. images), but when it comes to video, there is a glitch.

I am using Chrome 41 on OSX.

This is the simplified code of my worker:

self.addEventListener('fetch', function(event) {
  var url = event.request.url;
  console.log('SW: fetch', url);
  if (/\.mp4$/.test(url)) {
    url = 'https://vjs.zencdn.net/v/oceans.mp4';
    var options = {
      credentials: 'include',
      mode: 'no-cors'
    };
    event.respondWith(fetch(url, options));
  }
});

And this is the simplified code in my HTML page:

navigator.serviceWorker.register('sw.js').then(function(reg) {
  console.info('ServiceWorker registration successful for', reg.scope);

  var video = document.createElement('video');
  video.src = '/video.mp4';
  video.controls = true;
  video.autoplay = true;
  video.onerror = function(err) {
    console.error('Video load fail', err);
  }
  video.onload = function(data) {
    console.info('Video load success');
  }
  document.body.appendChild(video);

}).catch(function(err) {
  console.error('ServiceWorker registration failed:', err);
});

The first time you load the page, the worker installs, so the video request is not caught, thus failing. But when you reload the page (without cleaning the cache), it is successfully caught, and the worker successfully loads the video (HTTP 200 in its inspector), but for some reason, the main page throws a net::ERR_FAILED.

And I cannot manually read/stream it, because it comes from a different origin, resulting in an "opaque" Response type: http://www.w3.org/TR/service-workers/#cross-origin-resources

UPDATE: updating to Chrome 42, the error is now HTTP 400 Service Worker Fallback Required (from ServiceWorker). Strange thing is that the source code (line 510) indicates that it should only be raised when CORS headers are missing, but here the mode is no-cors.

like image 723
antoine129 Avatar asked Apr 16 '15 10:04

antoine129


People also ask

What does it mean to stream a response?

Streaming responses are the gold standard here: they allow the page that made the original request to start working with the response as soon as the first chunk of data is available, and potentially use parsers that are optimized for streaming to progressively display the content.

What can you do with service workers?

Using a Service worker you can easily set an app up to use cached assets first, thus providing a default experience even when offline, before then getting more data from the network (commonly known as Offline First).

What is stream in web?

The Streams API allows you to programmatically access streams of data received over the network or created by whatever means locally and process them with JavaScript. Streaming involves breaking down a resource that you want to receive, send, or transform into small chunks, and then processing these chunks bit by bit.


1 Answers

First off, I'd recommend trying in the current Chrome Canary, which currently corresponds to release 44.0.2371.0. The developer tooling around service workers continues to improve with each new Chrome release, and it's gotten much better with version 43+ in general.

It doesn't look like opaque Response objects can be used for the src of a <video> element, and I'm assuming that's deliberate. (Opaque Responses can be used for certain purposes, and I'm afraid I don't have a complete rundown of what will and won't work with an opaque Response.)

But in any case, you're fortunate in that vjs.zencdn.net supports CORS, so you can get away with using the default, CORS-enabled Request, which will give you a non-opaque Response.

One thing that you can't do is use credentials: 'include' in your CORS Request, because it will lead to a Fetch API cannot load https://vjs.zencdn.net/v/oceans.mp4. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. error. This doesn't seem like it's an issue with your particular host, since credentials aren't required.

When I switched your code to use event.respondWith(fetch('https://vjs.zencdn.net/v/oceans.mp4')); everything worked fine.

like image 74
Jeff Posnick Avatar answered Sep 19 '22 20:09

Jeff Posnick