From 3a7d96443c60ae36e065810a63dce06c14147754 Mon Sep 17 00:00:00 2001 From: Alf Date: Mon, 21 Dec 2020 22:38:14 -0800 Subject: [PATCH 1/5] Refactor to load wasm module in a web worker to prevent blocking main browser thread. Update FS to use WORKERFS. --- Dockerfile | 4 ++-- Makefile | 3 ++- src/ffprobe-wasm-wrapper.cpp | 20 +++++++++----------- www/public/index.html | 1 - www/src/components/File.vue | 29 +++++++++-------------------- www/src/components/Overview.vue | 17 ++++------------- 6 files changed, 26 insertions(+), 48 deletions(-) diff --git a/Dockerfile b/Dockerfile index 32f26f8..94382f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM emscripten/emsdk as build +FROM emscripten/emsdk:2.0.11 as build ARG FFMPEG_VERSION=4.3.1 ARG X264_VERSION=20170226-2245-stable @@ -30,7 +30,7 @@ RUN cd /tmp/ && \ wget http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \ tar zxf ffmpeg-${FFMPEG_VERSION}.tar.gz && rm ffmpeg-${FFMPEG_VERSION}.tar.gz -ARG CFLAGS="-s USE_PTHREADS -O3 -I${PREFIX}/include" +ARG CFLAGS="-s USE_PTHREADS=1 -O3 -I${PREFIX}/include" ARG LDFLAGS="$CFLAGS -L${PREFIX}/lib -s INITIAL_MEMORY=33554432" # Compile ffmpeg. diff --git a/Makefile b/Makefile index 79177bc..52b0dfb 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ dist/ffprobe-wasm.js: -I/opt/ffmpeg/include/ \ -s EXTRA_EXPORTED_RUNTIME_METHODS="[FS, cwrap, ccall, getValue, setValue, writeAsciiToMemory]" \ -s INITIAL_MEMORY=268435456 \ + -lavcodec -lavformat -lavfilter -lavdevice -lswresample -lswscale -lavutil -lm -lx264 \ -pthread \ - -lavcodec -lavformat -lavfilter -lavdevice -lswresample -lswscale -lavutil -lm -lx264 -pthread \ + -lworkerfs.js \ -o dist/ffprobe-wasm.js \ src/ffprobe-wasm-wrapper.cpp \ No newline at end of file diff --git a/src/ffprobe-wasm-wrapper.cpp b/src/ffprobe-wasm-wrapper.cpp index 054de73..5cc1c4b 100644 --- a/src/ffprobe-wasm-wrapper.cpp +++ b/src/ffprobe-wasm-wrapper.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -65,8 +66,8 @@ typedef struct FramesResponse { int nb_frames; } FramesResponse; -FileInfoResponse get_file_info() { - FILE *file = fopen("file", "rb"); +FileInfoResponse get_file_info(std::string filename) { + FILE *file = fopen(filename.c_str(), "rb"); if (!file) { printf("cannot open file\n"); } @@ -79,9 +80,8 @@ FileInfoResponse get_file_info() { // Open the file and read header. int ret; - if ((ret = avformat_open_input(&pFormatContext, "file", NULL, NULL)) < 0) { - printf("ERROR: could not open the file. Error: %d\n", ret); - printf("%s", av_err2str(ret)); + if ((ret = avformat_open_input(&pFormatContext, filename.c_str(), NULL, NULL)) < 0) { + printf("ERROR: %s\n", av_err2str(ret)); } // Get stream info from format. @@ -135,10 +135,10 @@ FileInfoResponse get_file_info() { return r; } -FramesResponse get_frames(int offset) { +FramesResponse get_frames(std::string filename, int offset) { av_log_set_level(AV_LOG_QUIET); // No logging output for libav. - FILE *file = fopen("file", "rb"); + FILE *file = fopen(filename.c_str(), "rb"); if (!file) { printf("cannot open file\n"); } @@ -151,9 +151,8 @@ FramesResponse get_frames(int offset) { // Open the file and read header. int ret; - if ((ret = avformat_open_input(&pFormatContext, "file", NULL, NULL)) < 0) { - printf("ERROR: could not open the file. Error: %d\n", ret); - printf("%s", av_err2str(ret)); + if ((ret = avformat_open_input(&pFormatContext, filename.c_str(), NULL, NULL)) < 0) { + printf("ERROR: %s\n", av_err2str(ret)); } // Get stream info from format. @@ -239,7 +238,6 @@ FramesResponse get_frames(int offset) { return r; } - EMSCRIPTEN_BINDINGS(constants) { function("avformat_version", &c_avformat_version); function("avcodec_version", &c_avcodec_version); diff --git a/www/public/index.html b/www/public/index.html index 06fa9ec..4123528 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -12,7 +12,6 @@ We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
- diff --git a/www/src/components/File.vue b/www/src/components/File.vue index 79f5c05..bcd57a1 100644 --- a/www/src/components/File.vue +++ b/www/src/components/File.vue @@ -18,12 +18,12 @@ :value="progress" max="100"> -
+
Selected file: {{ file ? `${file.name}: ${file.size} bytes` : '' }}
-
+
@@ -39,6 +39,8 @@ import Overview from './Overview.vue'; import Frames from './Frames.vue'; +const worker = new Worker('ffprobe-worker.js'); + export default { name: 'File', components: { @@ -49,13 +51,14 @@ export default { return { file: null, data: null, + info: null, progress: 0, showProgress: false, } }, - computed: { - info() { - return this.data && window.Module.get_file_info(); + created() { + worker.onmessage = (e) => { + this.info = e.data; } }, methods: { @@ -65,21 +68,7 @@ export default { this.showProgress = true; const file = event.dataTransfer ? event.dataTransfer.files[0] : event.target.files[0]; - const reader = new FileReader(); - - // reader.onload = e => this.$emit("load", event.target.result); - reader.onload = (event) => { - this.progress = 100; - this.data = new Uint8Array(event.target.result); - window.Module.FS.writeFile('file', new Uint8Array(this.data)); - setTimeout(() => { this.showProgress = false; }, 2000); - } - reader.onprogress = (event) => { - if (event.lengthComputable) { - this.progress = parseInt(((event.loaded / event.total) * 100), 10); - } - } - reader.readAsArrayBuffer(file); + worker.postMessage([ file ]); } } } diff --git a/www/src/components/Overview.vue b/www/src/components/Overview.vue index 4c8244d..92ae256 100644 --- a/www/src/components/Overview.vue +++ b/www/src/components/Overview.vue @@ -7,7 +7,7 @@

Streams

- +
@@ -15,15 +15,6 @@ export default { name: 'Overview', props: ['info'], - computed: { - streams() { - const s = []; - for (let i = 0; i < this.info.streams.size(); i++) { - s.push(this.info.streams.get(i)); - } - return s; - } - }, data() { return { items: [ @@ -38,9 +29,9 @@ export default { ], versions: [ { - libavutil: window.Module.avutil_version(), - libavcodec: window.Module.avcodec_version(), - libavformat: window.Module.avformat_version(), + libavutil: this.info.versions.libavutil, + libavcodec: this.info.versions.libavcodec, + libavformat: this.info.versions.libavformat, } ] } From bb7fb8e4ac00fc8b02f7f49318f2482189c5c32a Mon Sep 17 00:00:00 2001 From: Alf Date: Mon, 21 Dec 2020 22:41:30 -0800 Subject: [PATCH 2/5] Add ffprobe-worker. --- www/public/ffprobe-worker.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 www/public/ffprobe-worker.js diff --git a/www/public/ffprobe-worker.js b/www/public/ffprobe-worker.js new file mode 100644 index 0000000..8afc5a1 --- /dev/null +++ b/www/public/ffprobe-worker.js @@ -0,0 +1,36 @@ +onmessage = (e) => { + const file = e.data[0]; + + // Mount FS for files. + if (!FS.analyzePath('/work').exists) { + FS.mkdir('/work'); + } + FS.mount(WORKERFS, { files: [file] }, '/work'); + + // Call the wasm module. + const info = Module.get_file_info('/work/' + file.name); + + // Remap streams into collection. + const s = []; + for (let i = 0; i < info.streams.size(); i++) { + s.push(info.streams.get(i)); + } + + const versions = { + libavutil: Module.avutil_version(), + libavcodec: Module.avcodec_version(), + libavformat: Module.avformat_version(), + }; + + // Send back data response. + const data = { + ...info, + streams: s, + versions, + } + postMessage(data); + + // Cleanup mount. + FS.unmount('/work'); +} +self.importScripts('ffprobe-wasm.js'); // Load ffprobe into worker context. \ No newline at end of file From 60cb243a4287786d69a55a0dabc6238fcc6e2ba1 Mon Sep 17 00:00:00 2001 From: Alf Date: Mon, 21 Dec 2020 22:41:44 -0800 Subject: [PATCH 3/5] Update .gitignore. --- www/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/.gitignore b/www/.gitignore index 26d0a1d..2b323f1 100644 --- a/www/.gitignore +++ b/www/.gitignore @@ -22,5 +22,5 @@ pnpm-debug.log* *.sln *.sw? -public/*.js +public/*-wasm*.js public/*.wasm \ No newline at end of file From 16d930c36e1b11f976511e38d409843667b2ac60 Mon Sep 17 00:00:00 2001 From: Alf Date: Mon, 21 Dec 2020 23:26:31 -0800 Subject: [PATCH 4/5] Update Frames component to use ffprobe-worker. --- www/public/ffprobe-worker.js | 92 ++++++++++++++++++++++++----------- www/src/components/File.vue | 8 ++- www/src/components/Frames.vue | 61 ++++++++++++----------- www/src/main.js | 3 ++ 4 files changed, 100 insertions(+), 64 deletions(-) diff --git a/www/public/ffprobe-worker.js b/www/public/ffprobe-worker.js index 8afc5a1..33c32e4 100644 --- a/www/public/ffprobe-worker.js +++ b/www/public/ffprobe-worker.js @@ -1,36 +1,72 @@ onmessage = (e) => { - const file = e.data[0]; + const type = e.data[0]; + const file = e.data[1]; - // Mount FS for files. - if (!FS.analyzePath('/work').exists) { - FS.mkdir('/work'); - } - FS.mount(WORKERFS, { files: [file] }, '/work'); + let data; - // Call the wasm module. - const info = Module.get_file_info('/work/' + file.name); + switch (type) { + case 'get_file_info': + // Mount FS for files. + if (!FS.analyzePath('/work').exists) { + FS.mkdir('/work'); + } + FS.mount(WORKERFS, { files: [file] }, '/work'); - // Remap streams into collection. - const s = []; - for (let i = 0; i < info.streams.size(); i++) { - s.push(info.streams.get(i)); + // Call the wasm module. + const info = Module.get_file_info('/work/' + file.name); + + // Remap streams into collection. + const s = []; + for (let i = 0; i < info.streams.size(); i++) { + s.push(info.streams.get(i)); + } + + const versions = { + libavutil: Module.avutil_version(), + libavcodec: Module.avcodec_version(), + libavformat: Module.avformat_version(), + }; + + // Send back data response. + data = { + ...info, + streams: s, + versions, + } + postMessage(data); + + // Cleanup mount. + FS.unmount('/work'); + break; + + case 'get_frames': + if (!FS.analyzePath('/work').exists) { + FS.mkdir('/work'); + } + FS.mount(WORKERFS, { files: [file] }, '/work'); + + const offset = e.data[2]; + const frames = Module.get_frames('/work/' + file.name, offset); + + // Remap frames into collection. + const f = []; + for (let i = 0; i < frames.frames.size(); i++) { + f.push(frames.frames.get(i)); + } + + data = { + ...frames, + frames: f, + } + postMessage(data); + + // Cleanup mount. + FS.unmount('/work'); + break; + + default: + break; } - const versions = { - libavutil: Module.avutil_version(), - libavcodec: Module.avcodec_version(), - libavformat: Module.avformat_version(), - }; - - // Send back data response. - const data = { - ...info, - streams: s, - versions, - } - postMessage(data); - - // Cleanup mount. - FS.unmount('/work'); } self.importScripts('ffprobe-wasm.js'); // Load ffprobe into worker context. \ No newline at end of file diff --git a/www/src/components/File.vue b/www/src/components/File.vue index bcd57a1..2a8b3e5 100644 --- a/www/src/components/File.vue +++ b/www/src/components/File.vue @@ -28,7 +28,7 @@
- +
@@ -39,8 +39,6 @@ import Overview from './Overview.vue'; import Frames from './Frames.vue'; -const worker = new Worker('ffprobe-worker.js'); - export default { name: 'File', components: { @@ -57,7 +55,7 @@ export default { } }, created() { - worker.onmessage = (e) => { + this.$worker.onmessage = (e) => { this.info = e.data; } }, @@ -68,7 +66,7 @@ export default { this.showProgress = true; const file = event.dataTransfer ? event.dataTransfer.files[0] : event.target.files[0]; - worker.postMessage([ file ]); + this.$worker.postMessage([ 'get_file_info', file ]); } } } diff --git a/www/src/components/Frames.vue b/www/src/components/Frames.vue index 7680b7f..2ef72c7 100644 --- a/www/src/components/Frames.vue +++ b/www/src/components/Frames.vue @@ -1,35 +1,39 @@