New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
read a large file and stream to rtmp #147
Comments
This link has detail for streaming segments https://blog.scottlogic.com/2020/11/23/ffmpeg-webassembly.html |
@1Syler that link only seems to explain how to get ffmpeg to output in chunks, not how to get Edit 1Reading more on Emscripten's filesystem API, found Edit 2If I'm not crazy, I think I've answered my own question by digging around this repo and core a bit on my own. Would love some feedback on this solution, but it seems roughly doable! The only blocker is that the core script would have to be built with WORKERFS explicitly opted into. (good opportunity for a small fork?) I couldn't come up with a way to write a JS stream to a file on the user's machine other than the experimental Any & all feedback welcome. High-level:
(near) full example// main.js
const avi = {
description: 'AVI Video File',
accept: { 'video/x-msvideo': ['.avi'] },
};
const mp4 = {
description: 'MP4 Video File',
accept: { 'video/mp4': ['.mp4'] },
};
const sourceFile = () => window.showOpenFilePicker({ types: [avi] })
.then(entry => entry.getFile());
const destWritable = () => window.showSaveFilePicker({ types: [mp4] })
.then(file => file.createWritable());
someButton.onclick = async () => {
const sourceFile = await sourceFile();
const destWritable = await destWritable();
// spawn the child thread that will do the transcoding
const childThread = new Worker('./ffworker.js');
// do the stuff
childThread.postMessage({sourceFile, destWritable});
// wrap up
childThread.onmessage = () => {
destWritable.close();
childThread.terminate();
};
}; // ffworker.js
const fs = require('fs');
const { createFFmpeg, fetchFile } = require('@ffmpeg/ffmpeg');
const ffmpeg = createFFmpeg({ log: true });
// this will be invoked when `main.js` calls `childThread.postMessage({...})`
onmessage = async ({sourceFile, destWritable}) => {
await ffmpeg.load();
// mount a `WORKERFS` file system to a directory
// and stick `sourceFile` into it
ffmpeg.FS('mkdir', 'working');
ffmpeg.FS(
'mount',
WORKERFS, // no clue how to import this, emscripten's docs are confusing
{ files: [sourceFile] },
'/working'
);
// do some magic here to:
// - transcode (normal or chunkwise)
// - write every output chunk to `destWritable`
// - when done do `postMessage()` to close `destWritable` and terminate the worker
}; |
For one of my projects, I ended up creating my own fork of ffmpeg.wasm-core to get WORKERFS opted into, if anyone's interested. To enable WORKERFS, I had to add To actually use WORKERFS, you need to call the library from a web worker. This required me to use the ffmpeg core library directly, since the ffmpeg.wasm wrapper library actually errors out right now if you use it from a worker, mostly due to its use of the document object, which doesn't exist in a worker context. The document object's only used to resolve urls as far as I could tell, so I got by without it. This involved copy/pasting most of the ffmpeg.wasm code, which you can see here for full details: https://github.com/animebook/animebook.github.io/blob/d20cea4d4823df07f6f0f3c2056979cd3c1444e4/extension/bg/js/ffmpeg.js If I had to update your ffworker.js example, it might look something like this with WORKERFS, and just the core library. Not a full working example, since there's more you'd need to do to wrap ffmpeg core, such as detecting when the ffmpeg command is done with log messages, but it should get the rough idea down. // ffworker.js example
self.importScripts(
'ffmpeg-core.js', // import ffmpeg core library
);
onmessage = async ({sourceFile, destWritable}) => {
const ffmpegCore = await load();
const FS = ffmpegCore.FS;
const WORKERFS = ffmpegCore.FS_filesystems.WORKERFS;
FS.mkdir('working');
// mount a `WORKERFS` file system to a directory
// and stick `sourceFile` into it
ffmpegCore.FS_mount(WORKERFS, { files: [ sourceFile ]}, '/working');
// do some magic here to:
// - transcode (normal or chunkwise)
// - write every output chunk to `destWritable`
// - when done do `postMessage()` to close `destWritable` and terminate the worker
};
async function load() {
// create ffmpeg core directly
return await createFFmpegCore({
mainScriptUrlOrBlob: '/full/path/to/ffmpeg-core.js',
locateFile: (path, prefix) => {
if (path.endsWith('ffmpeg-core.wasm')) {
return '/full/path/to/ffmpeg-core.wasm';
}
if (path.endsWith('ffmpeg-core.worker.js')) {
return '/full/path/to/ffmpeg-core.worker.js';
}
return prefix + path;
},
printErr: message => {}, // need to implement this to detect when ffmpeg's done, see how ffmpeg.wasm does it
print: message => {} // need to implement this to detect when ffmpeg's done, see how ffmpeg.wasm does it
});
} Also, this is probably something I only ran into in my project due to how my chrome extension is architected, but I ran into some permission problems with sending the file to WORKERFS due to how I was sending a file to an iframe, which then sent the file to a worker. I don't think anyone other than me is doing that, but if you get permission problems for any reason, I got around it by recreating the file before sending it anywhere. It appears to be a chromium bug with sending File objects across different contexts. https://github.com/animebook/animebook.github.io/blob/d20cea4d4823df07f6f0f3c2056979cd3c1444e4/extension/fg/frontend.js#L49-L53 const cloneFile = new File( [ file.slice( 0, file.size ) ], file.name, { type: file.type } ); |
hello @soameshi Thanks for sharing your work. Trying to compile locally with |
I personally had trouble getting docker to work at the time, and since my changes were small I used the github actions already being used with ffmpeg.wasm-core to compile the project e.g. https://github.com/animebook/ffmpeg.wasm-core/actions/runs/763252454 Probably not a long-term solution, but if you want to just play around to see what WORKERFS can do it's an option. I haven't tried to replicate the ubuntu setup the github actions uses yet. |
Thanks for the reply, good call on the github actions. And also thanks for sharing your project where you included the compiled WORKERFS wasm; a lot of really clever ideas in there. |
I am using
https://www.npmjs.com/package/read-blob
to read a large file from chunks ..
how can i use ffmpeg,js to stream these chunks to rtmp
The text was updated successfully, but these errors were encountered: