samme
samme2mo ago

Streaming video from browser to deno server

Trying to stream video from Chrome to my local Deno server but having trouble understanding how to pipe things up correctly. When I visit localhost:8000, which hits the /video endpont via the <video> element, the /receive endpoint throws this error:
TypeError: The destination writable stream closed before all the data could be piped to it.
at readableStreamPipeTo (ext:deno_web/06_streams.js:2784:24)
at ReadableStream.pipeTo (ext:deno_web/06_streams.js:5340:12)
TypeError: The destination writable stream closed before all the data could be piped to it.
at readableStreamPipeTo (ext:deno_web/06_streams.js:2784:24)
at ReadableStream.pipeTo (ext:deno_web/06_streams.js:5340:12)
Here's my setup: Pasted into Chrome console:
(async () => {
const video = document.querySelector('video');

if (!video) {
return;
}

const stream = video.captureStream();

if (!stream) {
return;
}

console.log('Streaming video...');

const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
await fetch('http://localhost:8000/receive', {
method: 'POST',
body: event.data
});
}
};

mediaRecorder.start(1000);

video.onended = () => {
mediaRecorder.stop();
console.log('Done streaming video');
};
})();
(async () => {
const video = document.querySelector('video');

if (!video) {
return;
}

const stream = video.captureStream();

if (!stream) {
return;
}

console.log('Streaming video...');

const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
await fetch('http://localhost:8000/receive', {
method: 'POST',
body: event.data
});
}
};

mediaRecorder.start(1000);

video.onended = () => {
mediaRecorder.stop();
console.log('Done streaming video');
};
})();
Deno server:
/** @jsx h */
import html, { h } from "https://deno.land/x/htm@0.2.3/mod.ts";

const writables = [] as WritableStream[];

Deno.serve({ port: 8000 }, async (request: Request) => {
if (request.url.endsWith("/receive")) {
await Promise.all(writables.map(async (writable) => {
await request.body?.pipeTo(writable)
}));

return new Response(null, { status: 200 });
}

if (request.url.endsWith("/video")) {
const headers = new Headers();
headers.set("Content-Type", "video/mp4");

const { readable, writable } = new TransformStream();

writables.push(writable);

return new Response(readable, { headers });
}

return html({
title: "Stream",
body: (
<body>
<video width="100%" height="100%" src="/video" autoplay controls></video>
</body>
),
});
});
/** @jsx h */
import html, { h } from "https://deno.land/x/htm@0.2.3/mod.ts";

const writables = [] as WritableStream[];

Deno.serve({ port: 8000 }, async (request: Request) => {
if (request.url.endsWith("/receive")) {
await Promise.all(writables.map(async (writable) => {
await request.body?.pipeTo(writable)
}));

return new Response(null, { status: 200 });
}

if (request.url.endsWith("/video")) {
const headers = new Headers();
headers.set("Content-Type", "video/mp4");

const { readable, writable } = new TransformStream();

writables.push(writable);

return new Response(readable, { headers });
}

return html({
title: "Stream",
body: (
<body>
<video width="100%" height="100%" src="/video" autoplay controls></video>
</body>
),
});
});
1 Reply
Deno AI Helper
Deno AI Helper2mo ago
I wasn't able to find the answer to your question in the official Deno documentation. However, based on the error message and the code you've provided, it seems like the issue might be related to the writable stream being closed before the data is fully piped to it. In the Deno server code, you're pushing writable streams to an array writables every time the /video endpoint is hit. However, it's not clear from the provided code how and when these writable streams are being closed. If a writable stream is closed prematurely, it could cause the TypeError you're seeing when you try to pipe to it. One thing to check is whether the writable streams in the writables array are being managed correctly. Ensure that they are not being closed before the piping is complete. Also, consider the lifecycle of these streams and how they should be cleaned up after they are no longer needed to prevent memory leaks. Please note that this advice is not based on the Deno documentation, and I would advise you to double-check the accuracy of the advice given. It's also a good idea to review the official Deno documentation or seek further assistance from the community if you continue to experience issues.