Transferring a Readable Stream from one browser tab to another in Chrome

Viewed 261

I have looked into a few approaches to transfer a readable stream from one tab in Chrome to another:

  1. Broadcast channel -- doesn't support transferrable objects in postMessage
  2. Sender window postMessages to a Shared Worker connected to the sender and receiver windows. Shared Worker then postMessages to the receiver window.

From all of the documentation I have read, option 2 should work. However, my proof of concept fails with "DOMException: chunk could not be cloned" when I try to call the pipeTo method on the readable stream received by the receiver window.

I suspect this is because there is an issue transferring a stream's data through an intermediary (the Shared Worker) to a final destination. When I perform the same steps below with an empty, new ReadableStream(), there is no DOMException, which leads to the conclusion that the process of transferring data in the stream is the source of the error. I am not sure if there is a way around this. I can't transform the readable stream within the shared worker because that will lock it, and locked streams cannot be transferred.

Note that my example uses a media stream to generate data on the readable stream. My particular use case is related to transferring audio between same-origin tabs, however I am trying to make this question generic to maximize its utility to others. Also, I am aware that MediaStreamTracks are now transferable via a new w3c standard, but no browsers support this as far as I am aware.

Example code:

//client.js
var myWorker = new SharedWorker("worker.js");
myWorker.port.onmessage = async (e) => {
    console.log(e);
    if (e.data === 'Trigger Transfer') {
        navigator.mediaDevices.getUserMedia({audio:true})
            .then(function(stream) {
                const audioTrack = stream.getAudioTracks()[0];
                const trackProcessor = new MediaStreamTrackProcessor({ track: audioTrack });
                const readable = trackProcessor.readable;
                console.log("here");
                myWorker.port.postMessage(readable, [readable]);
            })
            .catch(function(err) {
                console.log("Failed to get user media", err);
            });
    }
    else {
            if (e.data instanceof ReadableStream) {
            try {
                console.log(e.data.locked === false); //This prints true. Locking does not appear to be the problem.
                await e.data.pipeTo(new WritableStream()); //This line leads to "DOMException: chunk could not be cloned"
            } catch(e) {
                console.log(e);
            }
        }
    }
};
//shared-worker.js
onconnect = function(e) {
  var port = e.ports[0];
  setTimeout(()=>{
    port.postMessage("Trigger Transfer");
  }, 500);
  port.onmessage = function(e) {
    port.postMessage(e.data, [e.data]);
  }
}
0 Answers
Related