From ffd64c954e377b43d1c598074448efedeac2a13b Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 5 May 2021 21:27:00 +0100 Subject: [PATCH 01/43] Enable DASH --- Makefile | 27 ++++++++++++++--------- build/library.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 build/library.js diff --git a/Makefile b/Makefile index c3a7c61..94566af 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,15 @@ # . PRE_JS = build/pre.js +LIBRARY_JS = build/library.js POST_JS_SYNC = build/post-sync.js POST_JS_WORKER = build/post-worker.js COMMON_FILTERS = aresample scale crop overlay hstack vstack COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat +COMMON_MUXERS = dash COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png +COMMON_BSFS = vp9_superframe WEBM_MUXERS = webm ogg null WEBM_ENCODERS = libvpx_vp8 libopus @@ -172,9 +175,12 @@ FFMPEG_COMMON_ARGS = \ --disable-dxva2 \ --disable-vaapi \ --disable-vdpau \ + $(addprefix --enable-bsf=,$(COMMON_BSFS)) \ $(addprefix --enable-decoder=,$(COMMON_DECODERS)) \ $(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \ + $(addprefix --enable-muxer=,$(COMMON_MUXERS)) \ --enable-protocol=file \ + --enable-protocol=pipe \ $(addprefix --enable-filter=,$(COMMON_FILTERS)) \ --disable-bzlib \ --disable-iconv \ @@ -194,10 +200,9 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) --enable-libopus \ --enable-libvpx \ --extra-cflags="-s USE_ZLIB=1 -I../libvpx/dist/include" \ - --extra-ldflags="-L../libvpx/dist/lib" \ + --extra-ldflags="-r -L../libvpx/dist/lib" \ && \ - emmake make -j && \ - cp ffmpeg ffmpeg.bc + emmake make -j EXESUF=.bc build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ @@ -209,13 +214,15 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) --enable-libmp3lame \ --enable-libx264 \ --extra-cflags="-s USE_ZLIB=1 -I../lame/dist/include" \ - --extra-ldflags="-L../lame/dist/lib" \ + --extra-ldflags="-r -L../lame/dist/lib" \ && \ - emmake make -j && \ - cp ffmpeg ffmpeg.bc + emmake make -j EXESUF=.bc EMCC_COMMON_ARGS = \ -O3 \ + -s ASYNCIFY \ + -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async"]' \ + --js-library $(LIBRARY_JS) \ --closure 1 \ --memory-init-file 0 \ -s WASM=0 \ @@ -234,7 +241,7 @@ ffmpeg-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_SYNC) --post-js $(POST_JS_SYNC) \ $(EMCC_COMMON_ARGS) -ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) +ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_JS) emcc $(FFMPEG_WEBM_BC) $(WEBM_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ $(EMCC_COMMON_ARGS) @@ -242,9 +249,9 @@ ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) ffmpeg-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_SYNC) emcc $(FFMPEG_MP4_BC) $(MP4_SHARED_DEPS) \ --post-js $(POST_JS_SYNC) \ - $(EMCC_COMMON_ARGS) -O2 + $(EMCC_COMMON_ARGS) -ffmpeg-worker-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_WORKER) +ffmpeg-worker-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_JS) emcc $(FFMPEG_MP4_BC) $(MP4_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ - $(EMCC_COMMON_ARGS) -O2 + $(EMCC_COMMON_ARGS) diff --git a/build/library.js b/build/library.js new file mode 100644 index 0000000..ec318df --- /dev/null +++ b/build/library.js @@ -0,0 +1,56 @@ +mergeInto(LibraryManager.library, { + emscripten_stdin_async: function (buf, size) { + if (!self.video_started) { + const onmessage = self.onmessage; + self.video_queue = []; + self.video_handler = null; + self.video_buf = null; + self.video_size = null; + self.video_process = function () { + let processed = 0; + while ((self.video_queue.length > 0) && (self.video_size > 0)) { + const head = self.video_queue.shift(); + const take = Math.min(head.length, self.video_size); + // TOOD: Weird - Module.HEAPU8 is undefined! + Module['HEAPU8'].set(head.subarray(0, take), self.video_buf); + processed += take; + self.video_buf += take; + self.video_size -= take; + if (take < head.length) { + self.video_queue.unshift(head.subarray(take)); + } + } + if (processed > 0) { + const handler = self.video_handler; + self.video_handler = null; + self.video_buf = null; + self.video_size = null; + handler(processed); + } + }; + self.onmessage = function (e) { + const msg = e.data; + if (msg.type == 'video-data') { + self.video_queue.push(new Uint8Array(msg.data)); + console.log("GOT VIDEO DATA", self.video_queue.length); + if (self.video_handler) { + self.video_process(); + } + } else { + onmessage.apply(this, arguments); + } + }; + self.postMessage({type: 'start-video'}); + self.video_started = true; + } + return Asyncify.handleSleep(wakeUp => { + if (size <= 0) { + return wakeUp(0); + } + self.video_handler = wakeUp; + self.video_buf = buf; + self.video_size = size; + self.video_process(); + }); + } +}); From 71112c7dad27a9556d5776ab563cca3c834ba9b5 Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 5 May 2021 22:23:45 +0100 Subject: [PATCH 02/43] Enable async stdin --- Makefile | 4 ++++ build/ffmpeg-async-stdin.patch | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 build/ffmpeg-async-stdin.patch diff --git a/Makefile b/Makefile index 94566af..83d6e86 100644 --- a/Makefile +++ b/Makefile @@ -193,6 +193,8 @@ FFMPEG_COMMON_ARGS = \ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ + git reset --hard && \ + patch -p1 < ../ffmpeg-async-stdin.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-encoder=,$(WEBM_ENCODERS)) \ @@ -206,6 +208,8 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ + git reset --hard && \ + patch -p1 < ../ffmpeg-async-stdin.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-encoder=,$(MP4_ENCODERS)) \ diff --git a/build/ffmpeg-async-stdin.patch b/build/ffmpeg-async-stdin.patch new file mode 100644 index 0000000..30ab39c --- /dev/null +++ b/build/ffmpeg-async-stdin.patch @@ -0,0 +1,26 @@ +diff --git a/libavformat/file.c b/libavformat/file.c +index 08c7f8e6dd..3ce026d303 100644 +--- a/libavformat/file.c ++++ b/libavformat/file.c +@@ -38,6 +38,8 @@ + #include "os_support.h" + #include "url.h" + ++extern int emscripten_stdin_async(unsigned char* buf, int size); ++ + /* Some systems may not have S_ISFIFO */ + #ifndef S_ISFIFO + # ifdef S_IFIFO +@@ -111,7 +113,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) + FileContext *c = h->priv_data; + int ret; + size = FFMIN(size, c->blocksize); +- ret = read(c->fd, buf, size); ++ if (c->fd == 0) { ++ ret = emscripten_stdin_async(buf, size); ++ } else { ++ ret = read(c->fd, buf, size); ++ } + if (ret == 0 && c->follow) + return AVERROR(EAGAIN); + if (ret == 0) From b585b6602dcc75fda7141e2d3ad3c4d69f7c0a86 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 8 May 2021 00:04:50 +0100 Subject: [PATCH 03/43] Create FS which will send data over HTTP --- Makefile | 2 + build/ffmpeg-dash-nonstrict.patch | 13 +++ build/library.js | 144 +++++++++++++++++++++++++++++- 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 build/ffmpeg-dash-nonstrict.patch diff --git a/Makefile b/Makefile index 83d6e86..9610821 100644 --- a/Makefile +++ b/Makefile @@ -195,6 +195,7 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ git reset --hard && \ patch -p1 < ../ffmpeg-async-stdin.patch && \ + patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-encoder=,$(WEBM_ENCODERS)) \ @@ -210,6 +211,7 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ git reset --hard && \ patch -p1 < ../ffmpeg-async-stdin.patch && \ + patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-encoder=,$(MP4_ENCODERS)) \ diff --git a/build/ffmpeg-dash-nonstrict.patch b/build/ffmpeg-dash-nonstrict.patch new file mode 100644 index 0000000..331e911 --- /dev/null +++ b/build/ffmpeg-dash-nonstrict.patch @@ -0,0 +1,13 @@ +diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c +index 24d43c34ea..36df2ec91f 100644 +--- a/libavformat/dashenc.c ++++ b/libavformat/dashenc.c +@@ -1939,7 +1939,7 @@ AVOutputFormat ff_dash_muxer = { + .priv_data_size = sizeof(DASHContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_H264, +- .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE, ++ .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, + .init = dash_init, + .write_header = dash_write_header, + .write_packet = dash_write_packet, diff --git a/build/library.js b/build/library.js index ec318df..95d8f63 100644 --- a/build/library.js +++ b/build/library.js @@ -1,6 +1,141 @@ mergeInto(LibraryManager.library, { emscripten_stdin_async: function (buf, size) { if (!self.video_started) { + /*const outbound_dev = FS.makedev(64, 0); + FS.registerDevice(outbound_dev, { + open: function (stream) { + console.log("OPEN", stream); + } + + }); + FS.mkdev('/outbound-dev', outbound_dev);*/ + FS.mkdir('/outbound'); + const intercept = { + get: function (target, name, receiver) { + const r = Reflect.get(target, name, receiver); + if (r === undefined) { + console.log('Accessed missing property:', name); + } + return r; + } + }; + const files = new Set(); + const ops = new Proxy({ + mount: function (mount) { + //console.log("MOUNT CALLED"); + return ops.createNode(null, '/', ops.getMode('/')); + }, + createNode: function (parent, name, mode, dev) { + const node = FS.createNode(parent, name, mode, dev); + node.node_ops = ops.node_ops; + node.stream_ops = ops.stream_ops; + node.usedBytes = 0; + return node; + }, + getMode: function (path) { + //console.log("GETMODE", path); + return (path === '/' ? 0x40000 : 0x100000) | 0x777; + }, + realPath: function (node) { + const parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.reverse(); + return PATH.join.apply(null, parts); + }, + node_ops: new Proxy({ + getattr: function (node) { + const attr = {}; + attr.dev = 1; + attr.ino = node.id; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + attr.size = FS.isDir(node.mode) ? 4096 : node.usedBytes; + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr: function (node, attr) { + //console.log("SETATTR", node, attr); + }, + lookup: function (parent, name) { + //console.log("LOOKUP", name); + if (!files.has(name)) { + throw FS.genericErrors[{{{ cDefine('ENOENT') }}}]; + } + const path = PATH.join2(ops.realPath(parent), name); + const mode = ops.getMode(path); + return ops.createNode(parent, name, mode); + }, + mknod: function (parent, name, mode, dev) { + //console.log("MKNOD", name); + files.add(name); + return ops.createNode(parent, name, mode, dev); + }, + rename: function (old_node, new_dir, new_name) { + //console.log("RENAME", old_node.name, new_name); + files.delete(old_node.name); + old_node.parent.timestamp = Date.now(); + old_node.name = new_name; + //files.add(new_name); + } + }, intercept), + stream_ops: new Proxy({ + open: function (stream) { + console.log("OPEN", stream.path, self.upload_url(stream.node.name)); + // for .webm, .webm.tmp and .mpd.tmp we should open http connection + // we have 3 second window so need to configure ffmpeg to batch in + // < 3 second chunks + // try sending in our order, might have to delay initial till after + // first segment though + }, + llseek: function (stream, offset, whence) { + console.log("LLSEEK", stream.path, offset, whence); + const position = offset; + if (whence === {{{ cDefine('SEEK_CUR') }}}) { + position += stream.position; + } else if (whence === {{{ cDefine('SEEK_END') }}}) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new Fs.ErrnoError({{{ cDefine('EINVAL') }}}); + } + return position; + }, + write: function (stream, buffer, offset, length, position) { + console.log("WRITE", stream.path, offset, length, position); + if (!length) { + return 0; + } + const node = stream.node; + node.timestamp = Date.now(); + node.usedBytes = Math.max(node.usedBytes, position + length); + return length; + // for .webm, .webm.tmp and .mpd.tmp we should write to opened http connection + // we get init-stream0.webm + // then chunkstream0-0001.webm.tmp which is then renamed to chunkstream0-0001.webm + // then output.mpd.tmp which is then renamed to output.mpd + // we keep getting writes to output.mpd - should we resend it? + // what does youtube want? + // 1179 + // 1200 + }, + close: function (stream) { + console.log("CLOSE", stream.path); + // for .webm, .webm.tmp and .mpd.tmp we should close opened http connection + } + }, intercept) + }, intercept); + FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; self.video_queue = []; self.video_handler = null; @@ -11,7 +146,7 @@ mergeInto(LibraryManager.library, { while ((self.video_queue.length > 0) && (self.video_size > 0)) { const head = self.video_queue.shift(); const take = Math.min(head.length, self.video_size); - // TOOD: Weird - Module.HEAPU8 is undefined! + // TOOD: Weird - Module.HEAPU8 is undefined but [] access works! Module['HEAPU8'].set(head.subarray(0, take), self.video_buf); processed += take; self.video_buf += take; @@ -36,6 +171,13 @@ mergeInto(LibraryManager.library, { if (self.video_handler) { self.video_process(); } + } else if (msg.type === 'base-url') { + self.upload_url = function (name) { + if (name.endsWith('.webm') || name.endsWith('.tmp')) { + return msg.data + name.replace(/\.tmp$/, ''); + } + return null; + }; } else { onmessage.apply(this, arguments); } From 13cca784de00c1595244fcc96d9eaea5d78c0feb Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 8 May 2021 19:13:41 +0100 Subject: [PATCH 04/43] DASH encoding (404 from Youtube - will require API call) --- build/library.js | 59 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/build/library.js b/build/library.js index 95d8f63..42152bc 100644 --- a/build/library.js +++ b/build/library.js @@ -89,16 +89,40 @@ mergeInto(LibraryManager.library, { }, intercept), stream_ops: new Proxy({ open: function (stream) { - console.log("OPEN", stream.path, self.upload_url(stream.node.name)); + const url = self.upload_url(stream.node.name); + console.log("OPEN", stream.path, url); + if (url) { + const { readable, writable } = new TransformStream(); + stream.upload_writer = writable.getWriter(); + fetch(url, { + method: 'PUT', + body: readable, + headers: { + 'Content-Type': 'application/octet-stream' + } + }).then(response => { + if (!response.ok) { + console.error(response.statusText); + } + }).catch (err => { + console.error(err); + }); + } // for .webm, .webm.tmp and .mpd.tmp we should open http connection // we have 3 second window so need to configure ffmpeg to batch in // < 3 second chunks // try sending in our order, might have to delay initial till after // first segment though + // try with dash first but then try hls: we should be able to get it + // to generate m4s files (how? does dash include mp4?) + // does it just mark it and it's still actually webm??? + // browser can only generate webm/H264 so we need the container converted + // we could write our own HTTP handler which writes out the files + // and then run ffmpeg on it }, llseek: function (stream, offset, whence) { console.log("LLSEEK", stream.path, offset, whence); - const position = offset; + let position = offset; if (whence === {{{ cDefine('SEEK_CUR') }}}) { position += stream.position; } else if (whence === {{{ cDefine('SEEK_END') }}}) { @@ -107,31 +131,42 @@ mergeInto(LibraryManager.library, { } } if (position < 0) { - throw new Fs.ErrnoError({{{ cDefine('EINVAL') }}}); + throw new FS.ErrnoError({{{ cDefine('EINVAL') }}}); } return position; }, - write: function (stream, buffer, offset, length, position) { - console.log("WRITE", stream.path, offset, length, position); + write: function (stream, buffer, offset, length, position, canOwn) { + console.log("WRITE", stream.path, /*buffer,*/ offset, length, position); if (!length) { return 0; } const node = stream.node; node.timestamp = Date.now(); node.usedBytes = Math.max(node.usedBytes, position + length); + if (stream.upload_writer) { +#if ALLOW_MEMORY_GROWTH + if (buffer.buffer === HEAP8.buffer) { + canOwn = false; + } +#endif + if (canOwn) { + stream.upload_writer.write(buffer.subarray(offset, offset + length)); + } else { + stream.upload_writer.write(buffer.slice(offset, offset + length)); + } + } return length; - // for .webm, .webm.tmp and .mpd.tmp we should write to opened http connection // we get init-stream0.webm // then chunkstream0-0001.webm.tmp which is then renamed to chunkstream0-0001.webm // then output.mpd.tmp which is then renamed to output.mpd // we keep getting writes to output.mpd - should we resend it? // what does youtube want? - // 1179 - // 1200 }, close: function (stream) { console.log("CLOSE", stream.path); - // for .webm, .webm.tmp and .mpd.tmp we should close opened http connection + if (stream.upload_writer) { + stream.upload_writer.close(); + } } }, intercept) }, intercept); @@ -173,8 +208,10 @@ mergeInto(LibraryManager.library, { } } else if (msg.type === 'base-url') { self.upload_url = function (name) { - if (name.endsWith('.webm') || name.endsWith('.tmp')) { - return msg.data + name.replace(/\.tmp$/, ''); + if (name.endsWith('.webm') || + name.endsWith('.m4s') || + name.endsWith('.tmp')) { + return msg.data + name.replace(/\.tmp$/, '').replace(/\.m4s$/, '.mp4'); } return null; }; From cca888a9d83113f252b5bd1ba3aaa655548151ab Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 9 May 2021 07:42:54 +0100 Subject: [PATCH 05/43] async close --- Makefile | 6 ++-- ...sync-stdin.patch => ffmpeg-async-io.patch} | 15 +++++++-- build/library.js | 33 +++++++++++++------ 3 files changed, 38 insertions(+), 16 deletions(-) rename build/{ffmpeg-async-stdin.patch => ffmpeg-async-io.patch} (62%) diff --git a/Makefile b/Makefile index 9610821..9b3c904 100644 --- a/Makefile +++ b/Makefile @@ -194,7 +194,7 @@ FFMPEG_COMMON_ARGS = \ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ git reset --hard && \ - patch -p1 < ../ffmpeg-async-stdin.patch && \ + patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ @@ -210,7 +210,7 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ git reset --hard && \ - patch -p1 < ../ffmpeg-async-stdin.patch && \ + patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ @@ -227,7 +227,7 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) EMCC_COMMON_ARGS = \ -O3 \ -s ASYNCIFY \ - -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async"]' \ + -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async", "emscripten_close_async"]' \ --js-library $(LIBRARY_JS) \ --closure 1 \ --memory-init-file 0 \ diff --git a/build/ffmpeg-async-stdin.patch b/build/ffmpeg-async-io.patch similarity index 62% rename from build/ffmpeg-async-stdin.patch rename to build/ffmpeg-async-io.patch index 30ab39c..40ae781 100644 --- a/build/ffmpeg-async-stdin.patch +++ b/build/ffmpeg-async-io.patch @@ -1,17 +1,18 @@ diff --git a/libavformat/file.c b/libavformat/file.c -index 08c7f8e6dd..3ce026d303 100644 +index 08c7f8e6dd..35eea9ce94 100644 --- a/libavformat/file.c +++ b/libavformat/file.c -@@ -38,6 +38,8 @@ +@@ -38,6 +38,9 @@ #include "os_support.h" #include "url.h" +extern int emscripten_stdin_async(unsigned char* buf, int size); ++extern void emscripten_close_async(int fd); + /* Some systems may not have S_ISFIFO */ #ifndef S_ISFIFO # ifdef S_IFIFO -@@ -111,7 +113,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) +@@ -111,7 +114,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) FileContext *c = h->priv_data; int ret; size = FFMIN(size, c->blocksize); @@ -24,3 +25,11 @@ index 08c7f8e6dd..3ce026d303 100644 if (ret == 0 && c->follow) return AVERROR(EAGAIN); if (ret == 0) +@@ -266,6 +273,7 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence) + static int file_close(URLContext *h) + { + FileContext *c = h->priv_data; ++ emscripten_close_async(c->fd); + return close(c->fd); + } + diff --git a/build/library.js b/build/library.js index 42152bc..5db51dc 100644 --- a/build/library.js +++ b/build/library.js @@ -94,18 +94,12 @@ mergeInto(LibraryManager.library, { if (url) { const { readable, writable } = new TransformStream(); stream.upload_writer = writable.getWriter(); - fetch(url, { + stream.upload_promise = fetch(url, { method: 'PUT', body: readable, headers: { 'Content-Type': 'application/octet-stream' } - }).then(response => { - if (!response.ok) { - console.error(response.statusText); - } - }).catch (err => { - console.error(err); }); } // for .webm, .webm.tmp and .mpd.tmp we should open http connection @@ -161,12 +155,11 @@ mergeInto(LibraryManager.library, { // then output.mpd.tmp which is then renamed to output.mpd // we keep getting writes to output.mpd - should we resend it? // what does youtube want? + // to deal with errors we may have to store up all the writes and then + // keep retrying in close() }, close: function (stream) { console.log("CLOSE", stream.path); - if (stream.upload_writer) { - stream.upload_writer.close(); - } } }, intercept) }, intercept); @@ -231,5 +224,25 @@ mergeInto(LibraryManager.library, { self.video_size = size; self.video_process(); }); + }, + emscripten_close_async: function (fd) { + return Asyncify.handleSleep(wakeUp => { + const stream = FS.streams[fd]; + if (stream && stream.upload_promise) { + console.log("WAITING FOR RESPONSE"); + stream.upload_writer.close(); + // TODO: to handle errors we may need to buffer writes and do the actual request here + // so we can retry if necessary + stream.upload_promise.then(response => { + if (!response.ok) { + console.error(response.statusText); + } + wakeUp(); + }).catch (err => { + console.error(err); + wakeUp(); + }); + } + }); } }); From 871a7f7f898fcfb6b5b35da7335cae52d300949d Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 9 May 2021 18:09:12 +0100 Subject: [PATCH 06/43] Add aac as common encoder --- Makefile | 4 +++- build/library.js | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 9b3c904..6e15005 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ POST_JS_WORKER = build/post-worker.js COMMON_FILTERS = aresample scale crop overlay hstack vstack COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat COMMON_MUXERS = dash +COMMON_ENCODERS = aac COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png COMMON_BSFS = vp9_superframe @@ -22,7 +23,7 @@ WEBM_SHARED_DEPS = \ build/libvpx/dist/lib/libvpx.so MP4_MUXERS = mp4 mp3 null -MP4_ENCODERS = libx264 libmp3lame aac +MP4_ENCODERS = libx264 libmp3lame FFMPEG_MP4_BC = build/ffmpeg-mp4/ffmpeg.bc FFMPEG_MP4_PC_PATH = ../x264/dist/lib/pkgconfig MP4_SHARED_DEPS = \ @@ -176,6 +177,7 @@ FFMPEG_COMMON_ARGS = \ --disable-vaapi \ --disable-vdpau \ $(addprefix --enable-bsf=,$(COMMON_BSFS)) \ + $(addprefix --enable-encoder=,$(COMMON_ENCODERS)) \ $(addprefix --enable-decoder=,$(COMMON_DECODERS)) \ $(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \ $(addprefix --enable-muxer=,$(COMMON_MUXERS)) \ diff --git a/build/library.js b/build/library.js index 5db51dc..d6a9e64 100644 --- a/build/library.js +++ b/build/library.js @@ -92,7 +92,7 @@ mergeInto(LibraryManager.library, { const url = self.upload_url(stream.node.name); console.log("OPEN", stream.path, url); if (url) { - const { readable, writable } = new TransformStream(); + /*const { readable, writable } = new TransformStream(); stream.upload_writer = writable.getWriter(); stream.upload_promise = fetch(url, { method: 'PUT', @@ -100,7 +100,7 @@ mergeInto(LibraryManager.library, { headers: { 'Content-Type': 'application/octet-stream' } - }); + });*/ } // for .webm, .webm.tmp and .mpd.tmp we should open http connection // we have 3 second window so need to configure ffmpeg to batch in @@ -242,6 +242,8 @@ mergeInto(LibraryManager.library, { console.error(err); wakeUp(); }); + } else { + wakeUp(); } }); } From 60a6c95ed7e0c4580696a51edbe764d6da430ec8 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 9 May 2021 22:50:30 +0100 Subject: [PATCH 07/43] HLS working! --- Makefile | 2 +- build/library.js | 60 +++++++++++++++++------------------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 6e15005..375007f 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ POST_JS_WORKER = build/post-worker.js COMMON_FILTERS = aresample scale crop overlay hstack vstack COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat -COMMON_MUXERS = dash +COMMON_MUXERS = dash hls COMMON_ENCODERS = aac COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png COMMON_BSFS = vp9_superframe diff --git a/build/library.js b/build/library.js index d6a9e64..a924985 100644 --- a/build/library.js +++ b/build/library.js @@ -80,7 +80,7 @@ mergeInto(LibraryManager.library, { return ops.createNode(parent, name, mode, dev); }, rename: function (old_node, new_dir, new_name) { - //console.log("RENAME", old_node.name, new_name); + console.log("RENAME", old_node.name, new_name); files.delete(old_node.name); old_node.parent.timestamp = Date.now(); old_node.name = new_name; @@ -89,27 +89,9 @@ mergeInto(LibraryManager.library, { }, intercept), stream_ops: new Proxy({ open: function (stream) { - const url = self.upload_url(stream.node.name); - console.log("OPEN", stream.path, url); - if (url) { - /*const { readable, writable } = new TransformStream(); - stream.upload_writer = writable.getWriter(); - stream.upload_promise = fetch(url, { - method: 'PUT', - body: readable, - headers: { - 'Content-Type': 'application/octet-stream' - } - });*/ - } - // for .webm, .webm.tmp and .mpd.tmp we should open http connection - // we have 3 second window so need to configure ffmpeg to batch in - // < 3 second chunks - // try sending in our order, might have to delay initial till after - // first segment though - // try with dash first but then try hls: we should be able to get it - // to generate m4s files (how? does dash include mp4?) - // does it just mark it and it's still actually webm??? + stream.upload_url = self.upload_url(stream.node.name); + stream.upload_data = []; + console.log("OPEN", stream.path, stream.upload_url); // browser can only generate webm/H264 so we need the container converted // we could write our own HTTP handler which writes out the files // and then run ffmpeg on it @@ -137,16 +119,16 @@ mergeInto(LibraryManager.library, { const node = stream.node; node.timestamp = Date.now(); node.usedBytes = Math.max(node.usedBytes, position + length); - if (stream.upload_writer) { + if (stream.upload_data) { #if ALLOW_MEMORY_GROWTH if (buffer.buffer === HEAP8.buffer) { canOwn = false; } #endif if (canOwn) { - stream.upload_writer.write(buffer.subarray(offset, offset + length)); + stream.upload_data.push(buffer.subarray(offset, offset + length)); } else { - stream.upload_writer.write(buffer.slice(offset, offset + length)); + stream.upload_data.push(buffer.slice(offset, offset + length)); } } return length; @@ -201,10 +183,8 @@ mergeInto(LibraryManager.library, { } } else if (msg.type === 'base-url') { self.upload_url = function (name) { - if (name.endsWith('.webm') || - name.endsWith('.m4s') || - name.endsWith('.tmp')) { - return msg.data + name.replace(/\.tmp$/, '').replace(/\.m4s$/, '.mp4'); + if (name.endsWith('.ts') || name.endsWith('.tmp')) { + return msg.data + name.replace(/\.tmp$/, ''); } return null; }; @@ -228,23 +208,25 @@ mergeInto(LibraryManager.library, { emscripten_close_async: function (fd) { return Asyncify.handleSleep(wakeUp => { const stream = FS.streams[fd]; - if (stream && stream.upload_promise) { - console.log("WAITING FOR RESPONSE"); - stream.upload_writer.close(); - // TODO: to handle errors we may need to buffer writes and do the actual request here - // so we can retry if necessary - stream.upload_promise.then(response => { + if (stream && stream.upload_url) { + console.log("MAKING REQUEST TO", stream.upload_url); + // TODO: handle errors by retrying + fetch(stream.upload_url, { + mode: 'no-cors', + method: 'POST', + body: new Blob(stream.upload_data), + headers: { + 'Content-Type': 'application/octet-stream' + } + }).then(response => { if (!response.ok) { console.error(response.statusText); } - wakeUp(); }).catch (err => { console.error(err); - wakeUp(); }); - } else { - wakeUp(); } + wakeUp(); }); } }); From af813250c8bf90e6f2cd3c91cde2413d19a83364 Mon Sep 17 00:00:00 2001 From: David Halls Date: Mon, 10 May 2021 22:35:47 +0100 Subject: [PATCH 08/43] Reduce size of webm version --- Makefile | 18 +++++++++++------- build/library.js | 34 ++++++++-------------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 375007f..3a803d1 100644 --- a/Makefile +++ b/Makefile @@ -7,15 +7,16 @@ LIBRARY_JS = build/library.js POST_JS_SYNC = build/post-sync.js POST_JS_WORKER = build/post-worker.js -COMMON_FILTERS = aresample scale crop overlay hstack vstack -COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat -COMMON_MUXERS = dash hls +COMMON_FILTERS = +COMMON_DEMUXERS = matroska +COMMON_MUXERS = hls COMMON_ENCODERS = aac -COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png -COMMON_BSFS = vp9_superframe +COMMON_DECODERS = opus +COMMON_BSFS = -WEBM_MUXERS = webm ogg null -WEBM_ENCODERS = libvpx_vp8 libopus +WEBM_MUXERS = +WEBM_DECODERS = +WEBM_ENCODERS = FFMPEG_WEBM_BC = build/ffmpeg-webm/ffmpeg.bc FFMPEG_WEBM_PC_PATH = ../opus/dist/lib/pkgconfig WEBM_SHARED_DEPS = \ @@ -23,6 +24,7 @@ WEBM_SHARED_DEPS = \ build/libvpx/dist/lib/libvpx.so MP4_MUXERS = mp4 mp3 null +MP4_DECODERS = h264 MP4_ENCODERS = libx264 libmp3lame FFMPEG_MP4_BC = build/ffmpeg-mp4/ffmpeg.bc FFMPEG_MP4_PC_PATH = ../x264/dist/lib/pkgconfig @@ -200,6 +202,7 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ + $(addprefix --enable-decoder=,$(WEBM_DECODERS)) \ $(addprefix --enable-encoder=,$(WEBM_ENCODERS)) \ $(addprefix --enable-muxer=,$(WEBM_MUXERS)) \ --enable-libopus \ @@ -216,6 +219,7 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ + $(addprefix --enable-decoder=,$(MP4_DECODERS)) \ $(addprefix --enable-encoder=,$(MP4_ENCODERS)) \ $(addprefix --enable-muxer=,$(MP4_MUXERS)) \ --enable-gpl \ diff --git a/build/library.js b/build/library.js index a924985..7c5b47d 100644 --- a/build/library.js +++ b/build/library.js @@ -1,14 +1,6 @@ mergeInto(LibraryManager.library, { emscripten_stdin_async: function (buf, size) { if (!self.video_started) { - /*const outbound_dev = FS.makedev(64, 0); - FS.registerDevice(outbound_dev, { - open: function (stream) { - console.log("OPEN", stream); - } - - }); - FS.mkdev('/outbound-dev', outbound_dev);*/ FS.mkdir('/outbound'); const intercept = { get: function (target, name, receiver) { @@ -84,7 +76,6 @@ mergeInto(LibraryManager.library, { files.delete(old_node.name); old_node.parent.timestamp = Date.now(); old_node.name = new_name; - //files.add(new_name); } }, intercept), stream_ops: new Proxy({ @@ -92,9 +83,6 @@ mergeInto(LibraryManager.library, { stream.upload_url = self.upload_url(stream.node.name); stream.upload_data = []; console.log("OPEN", stream.path, stream.upload_url); - // browser can only generate webm/H264 so we need the container converted - // we could write our own HTTP handler which writes out the files - // and then run ffmpeg on it }, llseek: function (stream, offset, whence) { console.log("LLSEEK", stream.path, offset, whence); @@ -119,7 +107,7 @@ mergeInto(LibraryManager.library, { const node = stream.node; node.timestamp = Date.now(); node.usedBytes = Math.max(node.usedBytes, position + length); - if (stream.upload_data) { + if (stream.upload_url) { #if ALLOW_MEMORY_GROWTH if (buffer.buffer === HEAP8.buffer) { canOwn = false; @@ -132,16 +120,10 @@ mergeInto(LibraryManager.library, { } } return length; - // we get init-stream0.webm - // then chunkstream0-0001.webm.tmp which is then renamed to chunkstream0-0001.webm - // then output.mpd.tmp which is then renamed to output.mpd - // we keep getting writes to output.mpd - should we resend it? - // what does youtube want? - // to deal with errors we may have to store up all the writes and then - // keep retrying in close() }, close: function (stream) { - console.log("CLOSE", stream.path); + files.delete(stream.node.name); + console.log("CLOSE", stream.path, files.size); } }, intercept) }, intercept); @@ -210,20 +192,20 @@ mergeInto(LibraryManager.library, { const stream = FS.streams[fd]; if (stream && stream.upload_url) { console.log("MAKING REQUEST TO", stream.upload_url); - // TODO: handle errors by retrying fetch(stream.upload_url, { mode: 'no-cors', method: 'POST', - body: new Blob(stream.upload_data), + body: new Blob(stream.upload_data)/*, no-cors so we can't set octet-stream headers: { 'Content-Type': 'application/octet-stream' - } + }*/ }).then(response => { if (!response.ok) { - console.error(response.statusText); + // no-cors so response is opaque and ok will always be false + //console.error("RESPONSE NOT OK", stream.upload_url, response); } }).catch (err => { - console.error(err); + console.error("REQUEST ERROR", stream.upload_url, err); }); } wakeUp(); From 456dec27907000247fc5a6d96f557990cac965b3 Mon Sep 17 00:00:00 2001 From: David Halls Date: Tue, 11 May 2021 23:58:45 +0100 Subject: [PATCH 09/43] Update ffmpeg, put h264 decoder back in --- Makefile | 6 ++---- build/ffmpeg-async-io.patch | 2 +- build/ffmpeg-dash-nonstrict.patch | 13 ------------- build/ffmpeg-mp4 | 2 +- build/ffmpeg-webm | 2 +- 5 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 build/ffmpeg-dash-nonstrict.patch diff --git a/Makefile b/Makefile index 3a803d1..e6527c4 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ COMMON_FILTERS = COMMON_DEMUXERS = matroska COMMON_MUXERS = hls COMMON_ENCODERS = aac -COMMON_DECODERS = opus +COMMON_DECODERS = opus h264 COMMON_BSFS = WEBM_MUXERS = @@ -24,7 +24,7 @@ WEBM_SHARED_DEPS = \ build/libvpx/dist/lib/libvpx.so MP4_MUXERS = mp4 mp3 null -MP4_DECODERS = h264 +MP4_DECODERS = MP4_ENCODERS = libx264 libmp3lame FFMPEG_MP4_BC = build/ffmpeg-mp4/ffmpeg.bc FFMPEG_MP4_PC_PATH = ../x264/dist/lib/pkgconfig @@ -199,7 +199,6 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ git reset --hard && \ patch -p1 < ../ffmpeg-async-io.patch && \ - patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-decoder=,$(WEBM_DECODERS)) \ @@ -216,7 +215,6 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ git reset --hard && \ patch -p1 < ../ffmpeg-async-io.patch && \ - patch -p1 < ../ffmpeg-dash-nonstrict.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ $(addprefix --enable-decoder=,$(MP4_DECODERS)) \ diff --git a/build/ffmpeg-async-io.patch b/build/ffmpeg-async-io.patch index 40ae781..27b5d6c 100644 --- a/build/ffmpeg-async-io.patch +++ b/build/ffmpeg-async-io.patch @@ -1,5 +1,5 @@ diff --git a/libavformat/file.c b/libavformat/file.c -index 08c7f8e6dd..35eea9ce94 100644 +index 8303436be0..4aa206859e 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -38,6 +38,9 @@ diff --git a/build/ffmpeg-dash-nonstrict.patch b/build/ffmpeg-dash-nonstrict.patch deleted file mode 100644 index 331e911..0000000 --- a/build/ffmpeg-dash-nonstrict.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c -index 24d43c34ea..36df2ec91f 100644 ---- a/libavformat/dashenc.c -+++ b/libavformat/dashenc.c -@@ -1939,7 +1939,7 @@ AVOutputFormat ff_dash_muxer = { - .priv_data_size = sizeof(DASHContext), - .audio_codec = AV_CODEC_ID_AAC, - .video_codec = AV_CODEC_ID_H264, -- .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE, -+ .flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, - .init = dash_init, - .write_header = dash_write_header, - .write_packet = dash_write_packet, diff --git a/build/ffmpeg-mp4 b/build/ffmpeg-mp4 index 192d1d3..dc91b91 160000 --- a/build/ffmpeg-mp4 +++ b/build/ffmpeg-mp4 @@ -1 +1 @@ -Subproject commit 192d1d34eb3668fa27f433e96036340e1e5077a0 +Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 diff --git a/build/ffmpeg-webm b/build/ffmpeg-webm index 192d1d3..dc91b91 160000 --- a/build/ffmpeg-webm +++ b/build/ffmpeg-webm @@ -1 +1 @@ -Subproject commit 192d1d34eb3668fa27f433e96036340e1e5077a0 +Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 From 83535c329affde43f35147194cd7703c5c0246ae Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 12 May 2021 21:38:18 +0100 Subject: [PATCH 10/43] Compile to WASM --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e6527c4..0b8511c 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,7 @@ EMCC_COMMON_ARGS = \ --js-library $(LIBRARY_JS) \ --closure 1 \ --memory-init-file 0 \ - -s WASM=0 \ + -s WASM=1 \ -s WASM_ASYNC_COMPILATION=0 \ -s ASSERTIONS=0 \ -s EXIT_RUNTIME=1 \ From c88fdd00408dea1ee7fcdbfbae1bc5c76c4e46aa Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 12 May 2021 23:08:58 +0100 Subject: [PATCH 11/43] Tidy up library.js --- build/library.js | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/build/library.js b/build/library.js index 7c5b47d..cf79509 100644 --- a/build/library.js +++ b/build/library.js @@ -2,11 +2,11 @@ mergeInto(LibraryManager.library, { emscripten_stdin_async: function (buf, size) { if (!self.video_started) { FS.mkdir('/outbound'); - const intercept = { + const check_access = { get: function (target, name, receiver) { const r = Reflect.get(target, name, receiver); if (r === undefined) { - console.log('Accessed missing property:', name); + console.warning('Accessed missing property:', name); } return r; } @@ -14,7 +14,6 @@ mergeInto(LibraryManager.library, { const files = new Set(); const ops = new Proxy({ mount: function (mount) { - //console.log("MOUNT CALLED"); return ops.createNode(null, '/', ops.getMode('/')); }, createNode: function (parent, name, mode, dev) { @@ -25,7 +24,6 @@ mergeInto(LibraryManager.library, { return node; }, getMode: function (path) { - //console.log("GETMODE", path); return (path === '/' ? 0x40000 : 0x100000) | 0x777; }, realPath: function (node) { @@ -55,10 +53,8 @@ mergeInto(LibraryManager.library, { return attr; }, setattr: function (node, attr) { - //console.log("SETATTR", node, attr); }, lookup: function (parent, name) { - //console.log("LOOKUP", name); if (!files.has(name)) { throw FS.genericErrors[{{{ cDefine('ENOENT') }}}]; } @@ -67,25 +63,21 @@ mergeInto(LibraryManager.library, { return ops.createNode(parent, name, mode); }, mknod: function (parent, name, mode, dev) { - //console.log("MKNOD", name); files.add(name); return ops.createNode(parent, name, mode, dev); }, rename: function (old_node, new_dir, new_name) { - console.log("RENAME", old_node.name, new_name); files.delete(old_node.name); old_node.parent.timestamp = Date.now(); old_node.name = new_name; } - }, intercept), + }, check_access), stream_ops: new Proxy({ open: function (stream) { stream.upload_url = self.upload_url(stream.node.name); stream.upload_data = []; - console.log("OPEN", stream.path, stream.upload_url); }, llseek: function (stream, offset, whence) { - console.log("LLSEEK", stream.path, offset, whence); let position = offset; if (whence === {{{ cDefine('SEEK_CUR') }}}) { position += stream.position; @@ -100,7 +92,6 @@ mergeInto(LibraryManager.library, { return position; }, write: function (stream, buffer, offset, length, position, canOwn) { - console.log("WRITE", stream.path, /*buffer,*/ offset, length, position); if (!length) { return 0; } @@ -123,10 +114,9 @@ mergeInto(LibraryManager.library, { }, close: function (stream) { files.delete(stream.node.name); - console.log("CLOSE", stream.path, files.size); } - }, intercept) - }, intercept); + }, check_access) + }, check_access); FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; self.video_queue = []; @@ -138,8 +128,7 @@ mergeInto(LibraryManager.library, { while ((self.video_queue.length > 0) && (self.video_size > 0)) { const head = self.video_queue.shift(); const take = Math.min(head.length, self.video_size); - // TOOD: Weird - Module.HEAPU8 is undefined but [] access works! - Module['HEAPU8'].set(head.subarray(0, take), self.video_buf); + HEAPU8.set(head.subarray(0, take), self.video_buf); processed += take; self.video_buf += take; self.video_size -= take; @@ -159,7 +148,6 @@ mergeInto(LibraryManager.library, { const msg = e.data; if (msg.type == 'video-data') { self.video_queue.push(new Uint8Array(msg.data)); - console.log("GOT VIDEO DATA", self.video_queue.length); if (self.video_handler) { self.video_process(); } From c9c4f8f44cd4e3902bbc4c823ca1ba3e8f7dd36a Mon Sep 17 00:00:00 2001 From: David Halls Date: Fri, 14 May 2021 23:13:44 +0100 Subject: [PATCH 12/43] Start moving hls into separate build --- .gitmodules | 3 +++ Makefile | 4 ++-- build/ffmpeg-hls | 1 + build/{library.js => library-hls.js} | 0 4 files changed, 6 insertions(+), 2 deletions(-) create mode 160000 build/ffmpeg-hls rename build/{library.js => library-hls.js} (100%) diff --git a/.gitmodules b/.gitmodules index c5aa371..af90da9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ path = build/ffmpeg-mp4 url = https://git.ffmpeg.org/ffmpeg.git ignore = dirty +[submodule "build/ffmpeg-hls"] + path = build/ffmpeg-hls + url = https://git.ffmpeg.org/ffmpeg.git diff --git a/Makefile b/Makefile index 0b8511c..ecf3b70 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ COMMON_FILTERS = COMMON_DEMUXERS = matroska COMMON_MUXERS = hls COMMON_ENCODERS = aac -COMMON_DECODERS = opus h264 +COMMON_DECODERS = opus COMMON_BSFS = WEBM_MUXERS = @@ -24,7 +24,7 @@ WEBM_SHARED_DEPS = \ build/libvpx/dist/lib/libvpx.so MP4_MUXERS = mp4 mp3 null -MP4_DECODERS = +MP4_DECODERS = h264 MP4_ENCODERS = libx264 libmp3lame FFMPEG_MP4_BC = build/ffmpeg-mp4/ffmpeg.bc FFMPEG_MP4_PC_PATH = ../x264/dist/lib/pkgconfig diff --git a/build/ffmpeg-hls b/build/ffmpeg-hls new file mode 160000 index 0000000..dc91b91 --- /dev/null +++ b/build/ffmpeg-hls @@ -0,0 +1 @@ +Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 diff --git a/build/library.js b/build/library-hls.js similarity index 100% rename from build/library.js rename to build/library-hls.js From f59b28583e00c6d2dcd28134c0e782e9f4f643cc Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 15 May 2021 17:34:25 +0100 Subject: [PATCH 13/43] Create separate ffmpeg-hls --- .gitmodules | 1 + Makefile | 100 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/.gitmodules b/.gitmodules index af90da9..f1f103d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,4 @@ [submodule "build/ffmpeg-hls"] path = build/ffmpeg-hls url = https://git.ffmpeg.org/ffmpeg.git + ignore = dirty diff --git a/Makefile b/Makefile index ecf3b70..3d8362b 100644 --- a/Makefile +++ b/Makefile @@ -3,20 +3,16 @@ # . PRE_JS = build/pre.js -LIBRARY_JS = build/library.js POST_JS_SYNC = build/post-sync.js POST_JS_WORKER = build/post-worker.js -COMMON_FILTERS = -COMMON_DEMUXERS = matroska -COMMON_MUXERS = hls -COMMON_ENCODERS = aac -COMMON_DECODERS = opus -COMMON_BSFS = +# Components common to webm and mp4, not for hls +COMMON_FILTERS = aresample scale crop overlay hstack vstack +COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat +COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png -WEBM_MUXERS = -WEBM_DECODERS = -WEBM_ENCODERS = +WEBM_MUXERS = webm ogg null +WEBM_ENCODERS = libvpx_vp8 libopus FFMPEG_WEBM_BC = build/ffmpeg-webm/ffmpeg.bc FFMPEG_WEBM_PC_PATH = ../opus/dist/lib/pkgconfig WEBM_SHARED_DEPS = \ @@ -24,23 +20,35 @@ WEBM_SHARED_DEPS = \ build/libvpx/dist/lib/libvpx.so MP4_MUXERS = mp4 mp3 null -MP4_DECODERS = h264 -MP4_ENCODERS = libx264 libmp3lame +MP4_ENCODERS = libx264 libmp3lame aac FFMPEG_MP4_BC = build/ffmpeg-mp4/ffmpeg.bc FFMPEG_MP4_PC_PATH = ../x264/dist/lib/pkgconfig MP4_SHARED_DEPS = \ build/lame/dist/lib/libmp3lame.so \ build/x264/dist/lib/libx264.so -all: webm mp4 +LIBRARY_HLS_JS = build/library-hls.js +HLS_DEMUXERS = matroska +HLS_MUXERS = hls +HLS_DECODERS = opus # add h264 to get rid of DTS warnings but beware patents! +HLS_ENCODERS = aac +FFMPEG_HLS_BC = build/ffmpeg-hls/ffmpeg.bc +FFMPEG_HLS_PC_PATH = ../opus/dist/lib/pkgconfig +HLS_SHARED_DEPS = build/opus/dist/lib/libopus.so + +all: webm mp4 hls webm: ffmpeg-webm.js ffmpeg-worker-webm.js mp4: ffmpeg-mp4.js ffmpeg-worker-mp4.js +hls: ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm -clean: clean-js \ +clean: clean-js clean-wasm \ clean-opus clean-libvpx clean-ffmpeg-webm \ - clean-lame clean-x264 clean-ffmpeg-mp4 + clean-lame clean-x264 clean-ffmpeg-mp4 \ + clean-ffmpeg-hls clean-js: rm -f ffmpeg*.js +clean-wasm: + rm -f ffmpeg*.wasm clean-opus: cd build/opus && git clean -xdf clean-libvpx: @@ -53,6 +61,8 @@ clean-x264: cd build/x264 && git clean -xdf clean-ffmpeg-mp4: cd build/ffmpeg-mp4 && git clean -xdf +clean-ffmpeg-hls: + cd build/ffmpeg-hls && git clean -xdf build/opus/configure: cd build/opus && ./autogen.sh @@ -150,7 +160,7 @@ build/x264/dist/lib/libx264.so: # - # - # - -FFMPEG_COMMON_ARGS = \ +FFMPEG_COMMON_CORE_ARGS = \ --cc=emcc \ --ranlib=emranlib \ --enable-cross-compile \ @@ -178,14 +188,7 @@ FFMPEG_COMMON_ARGS = \ --disable-dxva2 \ --disable-vaapi \ --disable-vdpau \ - $(addprefix --enable-bsf=,$(COMMON_BSFS)) \ - $(addprefix --enable-encoder=,$(COMMON_ENCODERS)) \ - $(addprefix --enable-decoder=,$(COMMON_DECODERS)) \ - $(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \ - $(addprefix --enable-muxer=,$(COMMON_MUXERS)) \ --enable-protocol=file \ - --enable-protocol=pipe \ - $(addprefix --enable-filter=,$(COMMON_FILTERS)) \ --disable-bzlib \ --disable-iconv \ --disable-libxcb \ @@ -195,13 +198,16 @@ FFMPEG_COMMON_ARGS = \ --disable-xlib \ --enable-zlib +FFMPEG_COMMON_ARGS = \ + $(FFMPEG_COMMON_CORE_ARGS) \ + $(addprefix --enable-decoder=,$(COMMON_DECODERS)) \ + $(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \ + $(addprefix --enable-filter=,$(COMMON_FILTERS)) + build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ - git reset --hard && \ - patch -p1 < ../ffmpeg-async-io.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_WEBM_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ - $(addprefix --enable-decoder=,$(WEBM_DECODERS)) \ $(addprefix --enable-encoder=,$(WEBM_ENCODERS)) \ $(addprefix --enable-muxer=,$(WEBM_MUXERS)) \ --enable-libopus \ @@ -213,11 +219,8 @@ build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) cd build/ffmpeg-mp4 && \ - git reset --hard && \ - patch -p1 < ../ffmpeg-async-io.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_MP4_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_ARGS) \ - $(addprefix --enable-decoder=,$(MP4_DECODERS)) \ $(addprefix --enable-encoder=,$(MP4_ENCODERS)) \ $(addprefix --enable-muxer=,$(MP4_MUXERS)) \ --enable-gpl \ @@ -228,14 +231,28 @@ build/ffmpeg-mp4/ffmpeg.bc: $(MP4_SHARED_DEPS) && \ emmake make -j EXESUF=.bc +build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) + cd build/ffmpeg-hls && \ + git reset --hard && \ + patch -p1 < ../ffmpeg-async-io.patch && \ + EM_PKG_CONFIG_PATH=$(FFMPEG_HLS_PC_PATH) emconfigure ./configure \ + $(FFMPEG_COMMON_CORE_ARGS) \ + $(addprefix --enable-demuxer=,$(HLS_DEMUXERS)) \ + $(addprefix --enable-muxer=,$(HLS_MUXERS)) \ + $(addprefix --enable-decoder=,$(HLS_DECODERS)) \ + $(addprefix --enable-encoder=,$(HLS_ENCODERS)) \ + --enable-libopus \ + --enable-protocol=pipe \ + --extra-cflags="-s USE_ZLIB=1" \ + --extra-ldflags="-r" \ + && \ + emmake make -j EXESUF=.bc + EMCC_COMMON_ARGS = \ -O3 \ - -s ASYNCIFY \ - -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async", "emscripten_close_async"]' \ - --js-library $(LIBRARY_JS) \ --closure 1 \ --memory-init-file 0 \ - -s WASM=1 \ + -s WASM=0 \ -s WASM_ASYNC_COMPILATION=0 \ -s ASSERTIONS=0 \ -s EXIT_RUNTIME=1 \ @@ -251,7 +268,7 @@ ffmpeg-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_SYNC) --post-js $(POST_JS_SYNC) \ $(EMCC_COMMON_ARGS) -ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_JS) +ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) emcc $(FFMPEG_WEBM_BC) $(WEBM_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ $(EMCC_COMMON_ARGS) @@ -259,9 +276,18 @@ ffmpeg-worker-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_J ffmpeg-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_SYNC) emcc $(FFMPEG_MP4_BC) $(MP4_SHARED_DEPS) \ --post-js $(POST_JS_SYNC) \ - $(EMCC_COMMON_ARGS) + $(EMCC_COMMON_ARGS) -O2 -ffmpeg-worker-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_JS) +ffmpeg-worker-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_WORKER) emcc $(FFMPEG_MP4_BC) $(MP4_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ - $(EMCC_COMMON_ARGS) + $(EMCC_COMMON_ARGS) -O2 + +ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_HLS_JS) + emcc $(FFMPEG_HLS_BC) $(HLS_SHARED_DEPS) \ + --post-js $(POST_JS_WORKER) \ + $(EMCC_COMMON_ARGS) \ + --js-library $(LIBRARY_HLS_JS) \ + -s WASM=1 \ + -s ASYNCIFY \ + -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async", "emscripten_close_async"]' From f54ebc14a4f0b4b40e204648bcb87ff317b1aa22 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 15 May 2021 18:06:40 +0100 Subject: [PATCH 14/43] Remove more from hls version --- Makefile | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 3d8362b..9ba9d79 100644 --- a/Makefile +++ b/Makefile @@ -195,14 +195,14 @@ FFMPEG_COMMON_CORE_ARGS = \ --disable-lzma \ --disable-sdl2 \ --disable-securetransport \ - --disable-xlib \ - --enable-zlib + --disable-xlib FFMPEG_COMMON_ARGS = \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-decoder=,$(COMMON_DECODERS)) \ $(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \ - $(addprefix --enable-filter=,$(COMMON_FILTERS)) + $(addprefix --enable-filter=,$(COMMON_FILTERS)) \ + --enable-zlib build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS) cd build/ffmpeg-webm && \ @@ -241,28 +241,31 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) $(addprefix --enable-muxer=,$(HLS_MUXERS)) \ $(addprefix --enable-decoder=,$(HLS_DECODERS)) \ $(addprefix --enable-encoder=,$(HLS_ENCODERS)) \ + --disable-zlib \ --enable-libopus \ --enable-protocol=pipe \ - --extra-cflags="-s USE_ZLIB=1" \ --extra-ldflags="-r" \ && \ emmake make -j EXESUF=.bc -EMCC_COMMON_ARGS = \ +EMCC_COMMON_CORE_ARGS = \ -O3 \ --closure 1 \ --memory-init-file 0 \ - -s WASM=0 \ -s WASM_ASYNC_COMPILATION=0 \ -s ASSERTIONS=0 \ -s EXIT_RUNTIME=1 \ - -s NODEJS_CATCH_EXIT=0 \ - -s NODEJS_CATCH_REJECTION=0 \ -s TOTAL_MEMORY=67108864 \ - -lnodefs.js -lworkerfs.js \ --pre-js $(PRE_JS) \ -o $@ +EMCC_COMMON_ARGS = \ + $(EMCC_COMMON_CORE_ARGS) \ + -s NODEJS_CATCH_EXIT=0 \ + -s NODEJS_CATCH_REJECTION=0 \ + -lnodefs.js -lworkerfs.js \ + -s WASM=0 + ffmpeg-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_SYNC) emcc $(FFMPEG_WEBM_BC) $(WEBM_SHARED_DEPS) \ --post-js $(POST_JS_SYNC) \ @@ -286,7 +289,7 @@ ffmpeg-worker-mp4.js: $(FFMPEG_MP4_BC) $(PRE_JS) $(POST_JS_WORKER) ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_HLS_JS) emcc $(FFMPEG_HLS_BC) $(HLS_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ - $(EMCC_COMMON_ARGS) \ + $(EMCC_COMMON_CORE_ARGS) \ --js-library $(LIBRARY_HLS_JS) \ -s WASM=1 \ -s ASYNCIFY \ From b61c4357d0bbd55bf64da6a663569d5e817533af Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 15 May 2021 18:37:33 +0100 Subject: [PATCH 15/43] Remove mpeg2ts h264 and hevc deps --- Makefile | 1 + build/ffmpeg-hls-configure.patch | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 build/ffmpeg-hls-configure.patch diff --git a/Makefile b/Makefile index 9ba9d79..20386f3 100644 --- a/Makefile +++ b/Makefile @@ -235,6 +235,7 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) cd build/ffmpeg-hls && \ git reset --hard && \ patch -p1 < ../ffmpeg-async-io.patch && \ + patch -p1 < ../ffmpeg-hls-configure.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_HLS_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(HLS_DEMUXERS)) \ diff --git a/build/ffmpeg-hls-configure.patch b/build/ffmpeg-hls-configure.patch new file mode 100644 index 0000000..689a46e --- /dev/null +++ b/build/ffmpeg-hls-configure.patch @@ -0,0 +1,13 @@ +diff --git a/configure b/configure +index d7a3f507e8..746f130a0a 100755 +--- a/configure ++++ b/configure +@@ -3349,7 +3349,7 @@ mp3_demuxer_select="mpegaudio_parser" + mp3_muxer_select="mpegaudioheader" + mp4_muxer_select="mov_muxer" + mpegts_demuxer_select="iso_media" +-mpegts_muxer_select="ac3_parser adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf" ++mpegts_muxer_select="adts_muxer" + mpegtsraw_demuxer_select="mpegts_demuxer" + mxf_muxer_select="golomb pcm_rechunk_bsf" + mxf_d10_muxer_select="mxf_muxer" From 7e447b138b3fb8091181335c73765281546cd1a9 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 22 May 2021 00:03:20 +0100 Subject: [PATCH 16/43] Add stream end message --- build/library-hls.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/library-hls.js b/build/library-hls.js index cf79509..a44911f 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -119,6 +119,7 @@ mergeInto(LibraryManager.library, { }, check_access); FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; + self.video_ended = false; self.video_queue = []; self.video_handler = null; self.video_buf = null; @@ -136,7 +137,7 @@ mergeInto(LibraryManager.library, { self.video_queue.unshift(head.subarray(take)); } } - if (processed > 0) { + if ((processed > 0) || self.video_ended) { const handler = self.video_handler; self.video_handler = null; self.video_buf = null; @@ -158,6 +159,11 @@ mergeInto(LibraryManager.library, { } return null; }; + } else if (msg.type == 'video-ended') { + self.video_ended = true; + if (self.video_handler) { + self.video_process(); + } } else { onmessage.apply(this, arguments); } From eeddb7b1866dc823bd66067ac5452f7e3bf1768c Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 29 May 2021 08:40:33 +0100 Subject: [PATCH 17/43] Add comments on Safari HLS support --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 20386f3..f68c66e 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,8 @@ MP4_SHARED_DEPS = \ build/x264/dist/lib/libx264.so LIBRARY_HLS_JS = build/library-hls.js -HLS_DEMUXERS = matroska +HLS_DEMUXERS = matroska # add mov for Safari support but beware patents! +HLS_BSFS = # add h264_mp4toannexb for Safari support but beware patents! HLS_MUXERS = hls HLS_DECODERS = opus # add h264 to get rid of DTS warnings but beware patents! HLS_ENCODERS = aac @@ -242,6 +243,7 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) $(addprefix --enable-muxer=,$(HLS_MUXERS)) \ $(addprefix --enable-decoder=,$(HLS_DECODERS)) \ $(addprefix --enable-encoder=,$(HLS_ENCODERS)) \ + $(addprefix --enable-bsf=,$(HLS_BSFS)) \ --disable-zlib \ --enable-libopus \ --enable-protocol=pipe \ From 376122ddbbfb0481745470d89a54b43851496895 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 6 Jun 2021 07:15:13 +0100 Subject: [PATCH 18/43] Support 2 async input files (WebCodecs support) --- Makefile | 2 +- build/ffmpeg-async-io.patch | 6 ++-- build/library-hls.js | 61 +++++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index f68c66e..56b6766 100644 --- a/Makefile +++ b/Makefile @@ -296,4 +296,4 @@ ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_J --js-library $(LIBRARY_HLS_JS) \ -s WASM=1 \ -s ASYNCIFY \ - -s 'ASYNCIFY_IMPORTS=["emscripten_stdin_async", "emscripten_close_async"]' + -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async"]' diff --git a/build/ffmpeg-async-io.patch b/build/ffmpeg-async-io.patch index 27b5d6c..dfd9742 100644 --- a/build/ffmpeg-async-io.patch +++ b/build/ffmpeg-async-io.patch @@ -6,7 +6,7 @@ index 8303436be0..4aa206859e 100644 #include "os_support.h" #include "url.h" -+extern int emscripten_stdin_async(unsigned char* buf, int size); ++extern int emscripten_read_async(int fd, unsigned char* buf, int size); +extern void emscripten_close_async(int fd); + /* Some systems may not have S_ISFIFO */ @@ -17,8 +17,8 @@ index 8303436be0..4aa206859e 100644 int ret; size = FFMIN(size, c->blocksize); - ret = read(c->fd, buf, size); -+ if (c->fd == 0) { -+ ret = emscripten_stdin_async(buf, size); ++ if ((c->fd == 3) || (c->fd == 4)) { ++ ret = emscripten_read_async(c->fd, buf, size); + } else { + ret = read(c->fd, buf, size); + } diff --git a/build/library-hls.js b/build/library-hls.js index a44911f..82f60af 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - emscripten_stdin_async: function (buf, size) { + emscripten_read_async: function (fd, buf, size) { if (!self.video_started) { FS.mkdir('/outbound'); const check_access = { @@ -120,37 +120,45 @@ mergeInto(LibraryManager.library, { FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; self.video_ended = false; - self.video_queue = []; - self.video_handler = null; - self.video_buf = null; - self.video_size = null; - self.video_process = function () { + self.video_queues = new Map(); + self.video_handlers = new Map(); + self.video_bufs = new Map(); + self.video_sizes = new Map(); + self.video_process = function (name) { let processed = 0; - while ((self.video_queue.length > 0) && (self.video_size > 0)) { - const head = self.video_queue.shift(); - const take = Math.min(head.length, self.video_size); - HEAPU8.set(head.subarray(0, take), self.video_buf); + while (self.video_queues.has(name) && + (self.video_queues.get(name).length > 0) && + (self.video_sizes.get(name) > 0)) { + const queue = self.video_queues.get(name); + const buf = self.video_bufs.get(name); + const size = self.video_sizes.get(name); + const head = queue.shift(); + const take = Math.min(head.length, size); + HEAPU8.set(head.subarray(0, take), buf); processed += take; - self.video_buf += take; - self.video_size -= take; + self.video_bufs.set(name, buf + take); + self.video_sizes.set(name, size - take); if (take < head.length) { - self.video_queue.unshift(head.subarray(take)); + queue.unshift(head.subarray(take)); } } if ((processed > 0) || self.video_ended) { - const handler = self.video_handler; - self.video_handler = null; - self.video_buf = null; - self.video_size = null; + const handler = self.video_handlers.get(name); + self.video_handlers.delete(name); + self.video_bufs.delete(name); + self.video_sizes.delete(name); handler(processed); } }; self.onmessage = function (e) { const msg = e.data; if (msg.type == 'video-data') { - self.video_queue.push(new Uint8Array(msg.data)); - if (self.video_handler) { - self.video_process(); + if (!self.video_queues.has(msg.name)) { + self.video_queues.set(msg.name, []); + } + self.video_queues.get(msg.name).push(new Uint8Array(msg.data)); + if (self.video_handlers.has(msg.name)) { + self.video_process(msg.name); } } else if (msg.type === 'base-url') { self.upload_url = function (name) { @@ -161,8 +169,8 @@ mergeInto(LibraryManager.library, { }; } else if (msg.type == 'video-ended') { self.video_ended = true; - if (self.video_handler) { - self.video_process(); + for (let h of self.video_handlers.keys()) { + self.video_process(h); } } else { onmessage.apply(this, arguments); @@ -175,10 +183,11 @@ mergeInto(LibraryManager.library, { if (size <= 0) { return wakeUp(0); } - self.video_handler = wakeUp; - self.video_buf = buf; - self.video_size = size; - self.video_process(); + const name = FS.streams[fd].node.name; + self.video_handlers.set(name, wakeUp); + self.video_bufs.set(name, buf); + self.video_sizes.set(name, size); + self.video_process(name); }); }, emscripten_close_async: function (fd) { From 4752a90c4e8cc3dbfecf680273b312fb2e128b58 Mon Sep 17 00:00:00 2001 From: David Halls Date: Mon, 5 Jul 2021 08:18:43 +0100 Subject: [PATCH 19/43] Add audio parsers --- Makefile | 8 +++- build/library-hls.js | 101 +++++++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index 56b6766..90479b4 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,13 @@ MP4_SHARED_DEPS = \ build/x264/dist/lib/libx264.so LIBRARY_HLS_JS = build/library-hls.js -HLS_DEMUXERS = matroska # add mov for Safari support but beware patents! +HLS_DEMUXERS = matroska pcm_f32le # add mov for Safari support but beware patents! HLS_BSFS = # add h264_mp4toannexb for Safari support but beware patents! HLS_MUXERS = hls -HLS_DECODERS = opus # add h264 to get rid of DTS warnings but beware patents! +HLS_DECODERS = opus pcm_f32le # add h264 to get rid of DTS warnings but beware patents! HLS_ENCODERS = aac +HLS_FILTERS = aresample +HLS_PARSERS = opus FFMPEG_HLS_BC = build/ffmpeg-hls/ffmpeg.bc FFMPEG_HLS_PC_PATH = ../opus/dist/lib/pkgconfig HLS_SHARED_DEPS = build/opus/dist/lib/libopus.so @@ -244,6 +246,8 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) $(addprefix --enable-decoder=,$(HLS_DECODERS)) \ $(addprefix --enable-encoder=,$(HLS_ENCODERS)) \ $(addprefix --enable-bsf=,$(HLS_BSFS)) \ + $(addprefix --enable-filter=,$(HLS_FILTERS)) \ + $(addprefix --enable-parser=,$(HLS_PARSERS)) \ --disable-zlib \ --enable-libopus \ --enable-protocol=pipe \ diff --git a/build/library-hls.js b/build/library-hls.js index 82f60af..c2f362d 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -1,6 +1,6 @@ mergeInto(LibraryManager.library, { emscripten_read_async: function (fd, buf, size) { - if (!self.video_started) { + if (!self.stream_started) { FS.mkdir('/outbound'); const check_access = { get: function (target, name, receiver) { @@ -119,75 +119,80 @@ mergeInto(LibraryManager.library, { }, check_access); FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; - self.video_ended = false; - self.video_queues = new Map(); - self.video_handlers = new Map(); - self.video_bufs = new Map(); - self.video_sizes = new Map(); - self.video_process = function (name) { + self.stream_ended = false; + self.stream_queues = new Map(); + self.stream_handlers = new Map(); + self.stream_bufs = new Map(); + self.stream_sizes = new Map(); + self.stream_process = function (name) { let processed = 0; - while (self.video_queues.has(name) && - (self.video_queues.get(name).length > 0) && - (self.video_sizes.get(name) > 0)) { - const queue = self.video_queues.get(name); - const buf = self.video_bufs.get(name); - const size = self.video_sizes.get(name); + while (self.stream_queues.has(name) && + (self.stream_queues.get(name).length > 0) && + (self.stream_sizes.get(name) > 0)) { + const queue = self.stream_queues.get(name); + const buf = self.stream_bufs.get(name); + const size = self.stream_sizes.get(name); const head = queue.shift(); const take = Math.min(head.length, size); HEAPU8.set(head.subarray(0, take), buf); processed += take; - self.video_bufs.set(name, buf + take); - self.video_sizes.set(name, size - take); + self.stream_bufs.set(name, buf + take); + self.stream_sizes.set(name, size - take); if (take < head.length) { queue.unshift(head.subarray(take)); } } - if ((processed > 0) || self.video_ended) { - const handler = self.video_handlers.get(name); - self.video_handlers.delete(name); - self.video_bufs.delete(name); - self.video_sizes.delete(name); + if ((processed > 0) || self.stream_ended) { + const handler = self.stream_handlers.get(name); + self.stream_handlers.delete(name); + self.stream_bufs.delete(name); + self.stream_sizes.delete(name); handler(processed); } }; self.onmessage = function (e) { - const msg = e.data; - if (msg.type == 'video-data') { - if (!self.video_queues.has(msg.name)) { - self.video_queues.set(msg.name, []); - } - self.video_queues.get(msg.name).push(new Uint8Array(msg.data)); - if (self.video_handlers.has(msg.name)) { - self.video_process(msg.name); - } - } else if (msg.type === 'base-url') { - self.upload_url = function (name) { - if (name.endsWith('.ts') || name.endsWith('.tmp')) { - return msg.data + name.replace(/\.tmp$/, ''); + const msg = e['data']; + switch (msg['type']) { + case 'stream-data': + if (!self.stream_queues.has(msg['name'])) { + self.stream_queues.set(msg['name'], []); } - return null; - }; - } else if (msg.type == 'video-ended') { - self.video_ended = true; - for (let h of self.video_handlers.keys()) { - self.video_process(h); - } - } else { - onmessage.apply(this, arguments); + self.stream_queues.get(msg['name']).push(new Uint8Array(msg['data'])); + if (self.stream_handlers.has(msg['name'])) { + self.stream_process(msg['name']); + } + break; + case 'base-url': + self.upload_url = function (name) { + if (name.endsWith('.ts') || name.endsWith('.tmp')) { + return msg['data'] + name.replace(/\.tmp$/, ''); + } + return null; + }; + break; + case 'stream-end': + self.stream_ended = true; + for (let h of self.stream_handlers.keys()) { + self.stream_process(h); + } + break; + default: + onmessage.apply(this, arguments); + break; } }; - self.postMessage({type: 'start-video'}); - self.video_started = true; + self.postMessage({type: 'start-stream'}); + self.stream_started = true; } return Asyncify.handleSleep(wakeUp => { if (size <= 0) { return wakeUp(0); } const name = FS.streams[fd].node.name; - self.video_handlers.set(name, wakeUp); - self.video_bufs.set(name, buf); - self.video_sizes.set(name, size); - self.video_process(name); + self.stream_handlers.set(name, wakeUp); + self.stream_bufs.set(name, buf); + self.stream_sizes.set(name, size); + self.stream_process(name); }); }, emscripten_close_async: function (fd) { From 93053ac3bfced1aaaafbd50078da7f44adbb2afd Mon Sep 17 00:00:00 2001 From: David Halls Date: Fri, 16 Jul 2021 07:15:42 +0100 Subject: [PATCH 20/43] Add ffexit message when exit() is called (exit is fired after async call so is no good) --- Makefile | 1 + build/ffmpeg-exit.patch | 20 ++++++++++++++++++++ build/library-hls.js | 7 +++++++ 3 files changed, 28 insertions(+) create mode 100644 build/ffmpeg-exit.patch diff --git a/Makefile b/Makefile index 90479b4..10f5ddc 100644 --- a/Makefile +++ b/Makefile @@ -239,6 +239,7 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) git reset --hard && \ patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-hls-configure.patch && \ + patch -p1 < ../ffmpeg-exit.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_HLS_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(HLS_DEMUXERS)) \ diff --git a/build/ffmpeg-exit.patch b/build/ffmpeg-exit.patch new file mode 100644 index 0000000..6fc545c --- /dev/null +++ b/build/ffmpeg-exit.patch @@ -0,0 +1,20 @@ +diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c +index fe424b6a4c..c9a8a7cb29 100644 +--- a/fftools/cmdutils.c ++++ b/fftools/cmdutils.c +@@ -130,12 +130,14 @@ void register_exit(void (*cb)(int ret)) + program_exit = cb; + } + ++int emscripten_exit(int code); ++ + void exit_program(int ret) + { + if (program_exit) + program_exit(ret); + +- exit(ret); ++ exit(emscripten_exit(ret)); + } + + double parse_number_or_die(const char *context, const char *numstr, int type, diff --git a/build/library-hls.js b/build/library-hls.js index c2f362d..de90706 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -218,5 +218,12 @@ mergeInto(LibraryManager.library, { } wakeUp(); }); + }, + emscripten_exit: function (code) { + self.postMessage({ + type: 'ffexit', + code + }); + return code; } }); From 1dd4fc0f64cddd894822f64f47abcba57fe3479c Mon Sep 17 00:00:00 2001 From: David Halls Date: Fri, 30 Jul 2021 07:37:08 +0100 Subject: [PATCH 21/43] Post message when fffmpeg starts producing data --- build/library-hls.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/library-hls.js b/build/library-hls.js index de90706..f6e62fd 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -99,6 +99,10 @@ mergeInto(LibraryManager.library, { node.timestamp = Date.now(); node.usedBytes = Math.max(node.usedBytes, position + length); if (stream.upload_url) { + if (!self.stream_sending) { + self.stream_sending = true; + self.postMessage({type: 'sending'}); + } #if ALLOW_MEMORY_GROWTH if (buffer.buffer === HEAP8.buffer) { canOwn = false; @@ -120,6 +124,7 @@ mergeInto(LibraryManager.library, { FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; self.stream_ended = false; + self.stream_sending = false; self.stream_queues = new Map(); self.stream_handlers = new Map(); self.stream_bufs = new Map(); From 71854336826e1ecd52722694ce4bea9449f2133d Mon Sep 17 00:00:00 2001 From: David Halls Date: Mon, 4 Oct 2021 23:20:18 +0100 Subject: [PATCH 22/43] libvpx 1.10.0 --- build/libvpx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/libvpx b/build/libvpx index 7ec7a33..b41ffb5 160000 --- a/build/libvpx +++ b/build/libvpx @@ -1 +1 @@ -Subproject commit 7ec7a33a081aeeb53fed1a8d87e4cbd189152527 +Subproject commit b41ffb53f1000ab2227c1736d8c1355aa5081c40 From f4a9e9b936395ce34121c225ac97ac160572a1f7 Mon Sep 17 00:00:00 2001 From: David Halls Date: Tue, 5 Oct 2021 08:11:26 +0100 Subject: [PATCH 23/43] Update README for HLS --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf28cfe..111e689 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Currently available builds (additional builds may be added in future): * `ffmpeg-worker-webm.js` - Web Worker version of `ffmpeg-webm.js`. * `ffmpeg-mp4.js` - MP4 encoding (H.264 & AAC & MP3 encoders, popular decoders). * `ffmpeg-worker-mp4.js` - Web Worker version of `ffmpeg-mp4.js`. +* `ffmpeg-worker-hls.js` - HLS (HTTP Live Streaming) in a Web Worker. Data is MPEG2-TS encoded and POSTed via HTTP. Note this uses Web Assembly. Note: only NPM releases contain abovementioned files. @@ -61,10 +62,41 @@ ffmpeg.js also provides wrapper for main function with Web Worker interface to o * `{type: "done", data: ""}` - Job finished with some result. * `{type: "error", data: ""}` - Error occurred. * `{type: "abort", data: ""}` - FFmpeg terminated abnormally (e.g. out of memory, wasm error). +* For HLS: + * `{type: "start-stream"} - FFmpeg is ready to stream data (application should start posting `stream-data` messages). + * `{type: "sending"} - Sent once when data starts to be POSTed. + * `{type: "ffexit", code: ""}` - FFmpeg exited with status code. You can send the following messages to the worker: * `{type: "run", ...opts}` - Start new job with provided options. - + * For HLS, you should typically send something based on this: + ```js + type: 'run', + arguments: [ + '-loglevel', 'debug', + '-seekable', '0', + '-i', '/work/stream1', + '-map', '0:v', + '-map', '0:a', + '-c:v', 'copy', // assumes video is already in desired encoding + '-c:a', 'copy', // assumes audio is already in desited encoding + '-f', 'hls', // use hls encoder + '-hls_time', '2', // 2 second HLS chunks + '-hls_segment_type', 'mpegts', // MPEG2-TS muxer + '-hls_list_size', '2', // two chunks in the list at a time + '-hls_flags', 'split_by_time', + '/outbound/output.m3u8' // path to media playlist file in virtual FS, + // must be under /outbound + ], + MEMFS: [ + { name: 'stream1' }, + { name: 'stream2' } + ] +``` +* For HLS: + * `{type: "base-url", upload_url: ""}` - Sets the URL to stream (POST) data to. The generated HLS chunk filenames are appended to this. Send this before sending any `stream-data`. + * `{type: "stream-data", name: "", data: ""}` - Data (audio/video/muxed) to supply to FFmpeg. `filename` must match an argument passed to FFmpeg via a `-i` option (up to two files are supported). `data` is an `ArrayBuffer`. + * `{type: "stream-end"}` - End all input streams (FFmpeg will exit after this). ```js const worker = new Worker("ffmpeg-worker-webm.js"); worker.onmessage = function(e) { From d23484b1efe3b70ecd443860de466ebcac8c0577 Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 7 Oct 2021 08:26:59 +0100 Subject: [PATCH 24/43] Update Dockerfile --- Dockerfile | 9 ++++----- README.md | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8cd8abc..a1b32e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ FROM ubuntu:rolling - -RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|mirror://mirrors.ubuntu.com/mirrors.txt|g' /etc/apt/sources.list \ - && apt-get update && apt-get install -y git python build-essential automake libtool pkg-config && apt-get clean \ +RUN apt-get update +RUN DEBIAN_FRONTEND="noninteractive" apt-get install -y tzdata +RUN apt-get install -y git python3 build-essential automake libtool pkg-config && apt-get clean \ && cd /root && git clone https://github.com/emscripten-core/emsdk.git \ - && cd /root/emsdk && ./emsdk install latest && ./emsdk activate latest \ - && sed -i 's|\]$|],"getrusage":["memset"]|' /root/emsdk/upstream/emscripten/src/deps_info.json \ No newline at end of file + && cd /root/emsdk && ./emsdk install latest && ./emsdk activate latest diff --git a/README.md b/README.md index 111e689..e4ab31f 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ It's recommended to use [Docker](https://www.docker.com/) to build ffmpeg.js. 3. Build everything: ```bash docker run --rm -it -v /path/to/ffmpeg.js:/mnt -w /opt kagamihi/ffmpeg.js - # cp -a /mnt/{.git,build,Makefile} . && source /root/emsdk/emsdk_env.sh && make && cp ffmpeg*.js /mnt + # cp -a /mnt/{.git,build,Makefile} . && source /root/emsdk/emsdk_env.sh && make && cp ffmpeg*.{js,wasm} /mnt ``` That's it. ffmpeg.js modules should appear in your repository clone. @@ -184,7 +184,7 @@ Ubuntu example: ```bash sudo apt-get update -sudo apt-get install -y git python build-essential automake libtool pkg-config +sudo apt-get install -y git python3 build-essential automake libtool pkg-config cd ~ git clone https://github.com/emscripten-core/emsdk.git && cd emsdk From 666ca5d3969d1a27ec63f6c4ed453c4d8fff5ec6 Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 14 Oct 2021 08:51:59 +0100 Subject: [PATCH 25/43] DASH live streaming support --- .gitmodules | 3 + Makefile | 40 ++++++++++-- build/ffmpeg-dash | 1 + build/ffmpeg-dash-codecs.patch | 101 ++++++++++++++++++++++++++++++ build/ffmpeg-dash-configure.patch | 12 ++++ build/library-dash.js | 1 + build/library-hls.js | 6 +- 7 files changed, 158 insertions(+), 6 deletions(-) create mode 160000 build/ffmpeg-dash create mode 100644 build/ffmpeg-dash-codecs.patch create mode 100644 build/ffmpeg-dash-configure.patch create mode 120000 build/library-dash.js diff --git a/.gitmodules b/.gitmodules index f1f103d..fe61236 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,6 @@ path = build/ffmpeg-hls url = https://git.ffmpeg.org/ffmpeg.git ignore = dirty +[submodule "build/ffmpeg-dash"] + path = build/ffmpeg-dash + url = https://git.ffmpeg.org/ffmpeg.git diff --git a/Makefile b/Makefile index 10f5ddc..e623088 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ PRE_JS = build/pre.js POST_JS_SYNC = build/post-sync.js POST_JS_WORKER = build/post-worker.js -# Components common to webm and mp4, not for hls +# Components common to webm and mp4, not for hls and dash COMMON_FILTERS = aresample scale crop overlay hstack vstack COMMON_DEMUXERS = matroska ogg mov mp3 wav image2 concat COMMON_DECODERS = vp8 h264 vorbis opus mp3 aac pcm_s16le mjpeg png @@ -31,7 +31,7 @@ LIBRARY_HLS_JS = build/library-hls.js HLS_DEMUXERS = matroska pcm_f32le # add mov for Safari support but beware patents! HLS_BSFS = # add h264_mp4toannexb for Safari support but beware patents! HLS_MUXERS = hls -HLS_DECODERS = opus pcm_f32le # add h264 to get rid of DTS warnings but beware patents! +HLS_DECODERS = libopus pcm_f32le # add h264 to get rid of DTS warnings but beware patents! HLS_ENCODERS = aac HLS_FILTERS = aresample HLS_PARSERS = opus @@ -39,15 +39,20 @@ FFMPEG_HLS_BC = build/ffmpeg-hls/ffmpeg.bc FFMPEG_HLS_PC_PATH = ../opus/dist/lib/pkgconfig HLS_SHARED_DEPS = build/opus/dist/lib/libopus.so -all: webm mp4 hls +LIBRARY_DASH_JS = build/library-dash.js +DASH_MUXERS = dash +FFMPEG_DASH_BC = build/ffmpeg-dash/ffmpeg.bc + +all: webm mp4 hls dash webm: ffmpeg-webm.js ffmpeg-worker-webm.js mp4: ffmpeg-mp4.js ffmpeg-worker-mp4.js hls: ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm +dash: ffmpeg-worker-dash.js ffmpeg-worker-dash.wasm clean: clean-js clean-wasm \ clean-opus clean-libvpx clean-ffmpeg-webm \ clean-lame clean-x264 clean-ffmpeg-mp4 \ - clean-ffmpeg-hls + clean-ffmpeg-hls clean-ffmpeg-dash clean-js: rm -f ffmpeg*.js clean-wasm: @@ -66,6 +71,8 @@ clean-ffmpeg-mp4: cd build/ffmpeg-mp4 && git clean -xdf clean-ffmpeg-hls: cd build/ffmpeg-hls && git clean -xdf +clean-ffmpeg-dash: + cd build/ffmpeg-dash && git clean -xdf build/opus/configure: cd build/opus && ./autogen.sh @@ -256,6 +263,22 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) && \ emmake make -j EXESUF=.bc +build/ffmpeg-dash/ffmpeg.bc: + cd build/ffmpeg-dash && \ + git reset --hard && \ + patch -p1 < ../ffmpeg-async-io.patch && \ + patch -p1 < ../ffmpeg-dash-configure.patch && \ + patch -p1 < ../ffmpeg-dash-codecs.patch && \ + patch -p1 < ../ffmpeg-exit.patch && \ + emconfigure ./configure \ + $(FFMPEG_COMMON_CORE_ARGS) \ + $(addprefix --enable-muxer=,$(DASH_MUXERS)) \ + --disable-zlib \ + --enable-protocol=pipe \ + --extra-ldflags="-r" \ + && \ + emmake make -j EXESUF=.bc + EMCC_COMMON_CORE_ARGS = \ -O3 \ --closure 1 \ @@ -302,3 +325,12 @@ ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_J -s WASM=1 \ -s ASYNCIFY \ -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async"]' + +ffmpeg-worker-dash.js ffmpeg-worker-dash.wasm: $(FFMPEG_DASH_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_DASH_JS) + emcc $(FFMPEG_DASH_BC) \ + --post-js $(POST_JS_WORKER) \ + $(EMCC_COMMON_CORE_ARGS) \ + --js-library $(LIBRARY_DASH_JS) \ + -s WASM=1 \ + -s ASYNCIFY \ + -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async"]' diff --git a/build/ffmpeg-dash b/build/ffmpeg-dash new file mode 160000 index 0000000..d115eec --- /dev/null +++ b/build/ffmpeg-dash @@ -0,0 +1 @@ +Subproject commit d115eec97929e23fd1b06df2d95f48cf5000eb87 diff --git a/build/ffmpeg-dash-codecs.patch b/build/ffmpeg-dash-codecs.patch new file mode 100644 index 0000000..09f9a1f --- /dev/null +++ b/build/ffmpeg-dash-codecs.patch @@ -0,0 +1,101 @@ +diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c +index 06bbf36c95..8ed9cd3a47 100644 +--- a/libavformat/dashenc.c ++++ b/libavformat/dashenc.c +@@ -363,83 +363,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, + } + return; + } +- +- // for codecs part of RFC 6381 +- if (par->codec_type == AVMEDIA_TYPE_VIDEO) +- tags[0] = ff_codec_movvideo_tags; +- else if (par->codec_type == AVMEDIA_TYPE_AUDIO) +- tags[0] = ff_codec_movaudio_tags; +- else +- return; +- +- tag = par->codec_tag; +- if (!tag) +- tag = av_codec_get_tag(tags, par->codec_id); +- if (!tag) +- return; +- if (size < 5) +- return; +- +- AV_WL32(str, tag); +- str[4] = '\0'; +- if (!strcmp(str, "mp4a") || !strcmp(str, "mp4v")) { +- uint32_t oti; +- tags[0] = ff_mp4_obj_type; +- oti = av_codec_get_tag(tags, par->codec_id); +- if (oti) +- av_strlcatf(str, size, ".%02"PRIx32, oti); +- else +- return; +- +- if (tag == MKTAG('m', 'p', '4', 'a')) { +- if (par->extradata_size >= 2) { +- int aot = par->extradata[0] >> 3; +- if (aot == 31) +- aot = ((AV_RB16(par->extradata) >> 5) & 0x3f) + 32; +- av_strlcatf(str, size, ".%d", aot); +- } +- } else if (tag == MKTAG('m', 'p', '4', 'v')) { +- // Unimplemented, should output ProfileLevelIndication as a decimal number +- av_log(s, AV_LOG_WARNING, "Incomplete RFC 6381 codec string for mp4v\n"); +- } +- } else if (!strcmp(str, "avc1")) { +- uint8_t *tmpbuf = NULL; +- uint8_t *extradata = par->extradata; +- int extradata_size = par->extradata_size; +- if (!extradata_size) +- return; +- if (extradata[0] != 1) { +- AVIOContext *pb; +- if (avio_open_dyn_buf(&pb) < 0) +- return; +- if (ff_isom_write_avcc(pb, extradata, extradata_size) < 0) { +- ffio_free_dyn_buf(&pb); +- return; +- } +- extradata_size = avio_close_dyn_buf(pb, &extradata); +- tmpbuf = extradata; +- } +- +- if (extradata_size >= 4) +- av_strlcatf(str, size, ".%02x%02x%02x", +- extradata[1], extradata[2], extradata[3]); +- av_free(tmpbuf); +- } else if (!strcmp(str, "av01")) { +- AV1SequenceParameters seq; +- if (!par->extradata_size) +- return; +- if (ff_av1_parse_seq_header(&seq, par->extradata, par->extradata_size) < 0) +- return; +- +- av_strlcatf(str, size, ".%01u.%02u%s.%02u", +- seq.profile, seq.level, seq.tier ? "H" : "M", seq.bitdepth); +- if (seq.color_description_present_flag) +- av_strlcatf(str, size, ".%01u.%01u%01u%01u.%02u.%02u.%02u.%01u", +- seq.monochrome, +- seq.chroma_subsampling_x, seq.chroma_subsampling_y, seq.chroma_sample_position, +- seq.color_primaries, seq.transfer_characteristics, seq.matrix_coefficients, +- seq.color_range); +- } + } + + static int flush_dynbuf(DASHContext *c, OutputStream *os, int *range_length) +diff --git a/libavformat/Makefile b/libavformat/Makefile +index c45caa3eed..d8d39d78bf 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -157,7 +157,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o + OBJS-$(CONFIG_CRC_MUXER) += crcenc.o + OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o + OBJS-$(CONFIG_DATA_MUXER) += rawenc.o +-OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o ++OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o vpcc.o + OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o + OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o + OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o diff --git a/build/ffmpeg-dash-configure.patch b/build/ffmpeg-dash-configure.patch new file mode 100644 index 0000000..8a714bf --- /dev/null +++ b/build/ffmpeg-dash-configure.patch @@ -0,0 +1,12 @@ +diff --git a/configure b/configure +index 92610c7edc..d30a5284c0 100755 +--- a/configure ++++ b/configure +@@ -3348,7 +3348,6 @@ avi_demuxer_select="riffdec exif" + avi_muxer_select="riffenc" + caf_demuxer_select="iso_media" + caf_muxer_select="iso_media" +-dash_muxer_select="mp4_muxer" + dash_demuxer_deps="libxml2" + dirac_demuxer_select="dirac_parser" + dts_demuxer_select="dca_parser" diff --git a/build/library-dash.js b/build/library-dash.js new file mode 120000 index 0000000..bcb2ef1 --- /dev/null +++ b/build/library-dash.js @@ -0,0 +1 @@ +library-hls.js \ No newline at end of file diff --git a/build/library-hls.js b/build/library-hls.js index f6e62fd..1dab7dd 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -167,14 +167,16 @@ mergeInto(LibraryManager.library, { self.stream_process(msg['name']); } break; - case 'base-url': + case 'base-url': { + const ext = msg['protocol'] === 'dash' ? '.webm' : '.ts'; self.upload_url = function (name) { - if (name.endsWith('.ts') || name.endsWith('.tmp')) { + if (name.endsWith(ext) || name.endsWith('.tmp')) { return msg['data'] + name.replace(/\.tmp$/, ''); } return null; }; break; + } case 'stream-end': self.stream_ended = true; for (let h of self.stream_handlers.keys()) { From 8f0bcd7913c6c63c6203620dd6f4f135e1f38d85 Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 14 Oct 2021 18:10:26 +0100 Subject: [PATCH 26/43] n4.4 for ffmpeg-dash --- build/ffmpeg-dash | 2 +- build/ffmpeg-dash-codecs.patch | 4 ++-- build/ffmpeg-dash-configure.patch | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/ffmpeg-dash b/build/ffmpeg-dash index d115eec..dc91b91 160000 --- a/build/ffmpeg-dash +++ b/build/ffmpeg-dash @@ -1 +1 @@ -Subproject commit d115eec97929e23fd1b06df2d95f48cf5000eb87 +Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 diff --git a/build/ffmpeg-dash-codecs.patch b/build/ffmpeg-dash-codecs.patch index 09f9a1f..a958bfa 100644 --- a/build/ffmpeg-dash-codecs.patch +++ b/build/ffmpeg-dash-codecs.patch @@ -2,7 +2,7 @@ diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 06bbf36c95..8ed9cd3a47 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c -@@ -363,83 +363,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, +@@ -375,83 +375,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, } return; } @@ -90,7 +90,7 @@ diff --git a/libavformat/Makefile b/libavformat/Makefile index c45caa3eed..d8d39d78bf 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile -@@ -157,7 +157,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o +@@ -153,7 +153,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER) += rawenc.o diff --git a/build/ffmpeg-dash-configure.patch b/build/ffmpeg-dash-configure.patch index 8a714bf..e435984 100644 --- a/build/ffmpeg-dash-configure.patch +++ b/build/ffmpeg-dash-configure.patch @@ -2,7 +2,7 @@ diff --git a/configure b/configure index 92610c7edc..d30a5284c0 100755 --- a/configure +++ b/configure -@@ -3348,7 +3348,6 @@ avi_demuxer_select="riffdec exif" +@@ -3313,7 +3313,6 @@ avi_demuxer_select="riffdec exif" avi_muxer_select="riffenc" caf_demuxer_select="iso_media" caf_muxer_select="iso_media" From f5f0d720906fc815894e78f04f1d919805256c18 Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 14 Oct 2021 18:11:24 +0100 Subject: [PATCH 27/43] Ignore ffmpeg-dash patches --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index fe61236..69d85d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,3 +29,4 @@ [submodule "build/ffmpeg-dash"] path = build/ffmpeg-dash url = https://git.ffmpeg.org/ffmpeg.git + ignore = dirty From 592497307a4cf1b002c2467ac616c18f719bd2d7 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 16 Oct 2021 08:29:36 +0100 Subject: [PATCH 28/43] Fixes for DASH --- Makefile | 14 +++++++++++++- build/library-hls.js | 13 +++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e623088..8e9b48a 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,13 @@ FFMPEG_HLS_PC_PATH = ../opus/dist/lib/pkgconfig HLS_SHARED_DEPS = build/opus/dist/lib/libopus.so LIBRARY_DASH_JS = build/library-dash.js -DASH_MUXERS = dash +DASH_DEMUXERS = matroska +DASH_BSFS = vp9_superframe +DASH_MUXERS = dash webm +DASH_DECODERS = +DASH_ENCODERS = +DASH_FILTERS = +DASH_PARSERS = vp9 opus FFMPEG_DASH_BC = build/ffmpeg-dash/ffmpeg.bc all: webm mp4 hls dash @@ -272,7 +278,13 @@ build/ffmpeg-dash/ffmpeg.bc: patch -p1 < ../ffmpeg-exit.patch && \ emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ + $(addprefix --enable-demuxer=,$(DASH_DEMUXERS)) \ $(addprefix --enable-muxer=,$(DASH_MUXERS)) \ + $(addprefix --enable-decoder=,$(DASH_DECODERS)) \ + $(addprefix --enable-encoder=,$(DASH_ENCODERS)) \ + $(addprefix --enable-bsf=,$(DASH_BSFS)) \ + $(addprefix --enable-filter=,$(DASH_FILTERS)) \ + $(addprefix --enable-parser=,$(DASH_PARSERS)) \ --disable-zlib \ --enable-protocol=pipe \ --extra-ldflags="-r" \ diff --git a/build/library-hls.js b/build/library-hls.js index 1dab7dd..297d430 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -6,7 +6,7 @@ mergeInto(LibraryManager.library, { get: function (target, name, receiver) { const r = Reflect.get(target, name, receiver); if (r === undefined) { - console.warning('Accessed missing property:', name); + console.warn('Accessed missing property:', name, target); } return r; } @@ -70,6 +70,9 @@ mergeInto(LibraryManager.library, { files.delete(old_node.name); old_node.parent.timestamp = Date.now(); old_node.name = new_name; + }, + unlink: function (parent, name) { + files.delete(name); } }, check_access), stream_ops: new Proxy({ @@ -168,10 +171,12 @@ mergeInto(LibraryManager.library, { } break; case 'base-url': { - const ext = msg['protocol'] === 'dash' ? '.webm' : '.ts'; self.upload_url = function (name) { - if (name.endsWith(ext) || name.endsWith('.tmp')) { - return msg['data'] + name.replace(/\.tmp$/, ''); + if (name.endsWith('.webm') || + name.endsWith('.m4s') || + name.endsWith('.ts') || + name.endsWith('.tmp')) { + return msg['data'] + name.replace(/\.tmp$/, '').replace(/\.m4s$/, '.mp4'); } return null; }; From dab111290bdabc36ce0f0f1267bd364136c87374 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 16 Oct 2021 21:02:50 +0100 Subject: [PATCH 29/43] Allow HTTP method to be specified for HLS and DASH --- build/library-hls.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/library-hls.js b/build/library-hls.js index 297d430..36d6acb 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -180,6 +180,7 @@ mergeInto(LibraryManager.library, { } return null; }; + self.upload_method = msg['method'] || 'POST'; break; } case 'stream-end': @@ -214,7 +215,7 @@ mergeInto(LibraryManager.library, { console.log("MAKING REQUEST TO", stream.upload_url); fetch(stream.upload_url, { mode: 'no-cors', - method: 'POST', + method: self.upload_method, body: new Blob(stream.upload_data)/*, no-cors so we can't set octet-stream headers: { 'Content-Type': 'application/octet-stream' From 2aee0a593ccb6da056618a89a6c43700cf8580a7 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 16 Oct 2021 21:51:01 +0100 Subject: [PATCH 30/43] Allow DASH and HLS upload options to be specified --- build/library-hls.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/library-hls.js b/build/library-hls.js index 36d6acb..fe7d819 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -180,7 +180,7 @@ mergeInto(LibraryManager.library, { } return null; }; - self.upload_method = msg['method'] || 'POST'; + self.upload_options = msg['options']; break; } case 'stream-end': @@ -213,14 +213,15 @@ mergeInto(LibraryManager.library, { const stream = FS.streams[fd]; if (stream && stream.upload_url) { console.log("MAKING REQUEST TO", stream.upload_url); - fetch(stream.upload_url, { + fetch(stream.upload_url, Object.assign({ mode: 'no-cors', - method: self.upload_method, - body: new Blob(stream.upload_data)/*, no-cors so we can't set octet-stream + method: 'POST', + /* no-cors so we can't set octet-stream headers: { 'Content-Type': 'application/octet-stream' }*/ - }).then(response => { + body: new Blob(stream.upload_data) + }, self.upload_options)).then(response => { if (!response.ok) { // no-cors so response is opaque and ok will always be false //console.error("RESPONSE NOT OK", stream.upload_url, response); From 95aeb4915ff179a742f7caf280d31d071153aae7 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 17 Oct 2021 07:59:59 +0100 Subject: [PATCH 31/43] Update README for DASH --- README.md | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e4ab31f..6cdd375 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Currently available builds (additional builds may be added in future): * `ffmpeg-mp4.js` - MP4 encoding (H.264 & AAC & MP3 encoders, popular decoders). * `ffmpeg-worker-mp4.js` - Web Worker version of `ffmpeg-mp4.js`. * `ffmpeg-worker-hls.js` - HLS (HTTP Live Streaming) in a Web Worker. Data is MPEG2-TS encoded and POSTed via HTTP. Note this uses Web Assembly. +* `ffmpeg-worker-dash.js` - DASH (Dynamic Adaptive Streaming over HTTP) in a Web Worker. Data is PUT or POSTED via HTTP. Note this uses Web Assembly. Note: only NPM releases contain abovementioned files. @@ -62,7 +63,7 @@ ffmpeg.js also provides wrapper for main function with Web Worker interface to o * `{type: "done", data: ""}` - Job finished with some result. * `{type: "error", data: ""}` - Error occurred. * `{type: "abort", data: ""}` - FFmpeg terminated abnormally (e.g. out of memory, wasm error). -* For HLS: +* For HLS and DASH: * `{type: "start-stream"} - FFmpeg is ready to stream data (application should start posting `stream-data` messages). * `{type: "sending"} - Sent once when data starts to be POSTed. * `{type: "ffexit", code: ""}` - FFmpeg exited with status code. @@ -73,18 +74,18 @@ You can send the following messages to the worker: ```js type: 'run', arguments: [ - '-loglevel', 'debug', '-seekable', '0', + '-loglevel', 'info', '-i', '/work/stream1', '-map', '0:v', '-map', '0:a', '-c:v', 'copy', // assumes video is already in desired encoding - '-c:a', 'copy', // assumes audio is already in desited encoding + '-c:a', 'copy', // assumes audio is already in desired encoding '-f', 'hls', // use hls encoder '-hls_time', '2', // 2 second HLS chunks '-hls_segment_type', 'mpegts', // MPEG2-TS muxer '-hls_list_size', '2', // two chunks in the list at a time - '-hls_flags', 'split_by_time', + '-hls_flags', 'split_by_time', // if you don't have < 2s keyframes '/outbound/output.m3u8' // path to media playlist file in virtual FS, // must be under /outbound ], @@ -93,10 +94,35 @@ You can send the following messages to the worker: { name: 'stream2' } ] ``` -* For HLS: - * `{type: "base-url", upload_url: ""}` - Sets the URL to stream (POST) data to. The generated HLS chunk filenames are appended to this. Send this before sending any `stream-data`. + * For DASH, you should typically send something based on this: + ```js + type: 'run', + arguments: [ + '-seekable', '0', + '-loglevel', 'info', + '-i', '/work/stream1', + '-map', '0:v', + '-map', '0:a', + '-c:v', 'copy', // assumes video is already in desired encoding + '-c:a', 'copy', // assumes audio is already in desired encoding + '-f', 'dash', // use dash encoder + '-seg_duration', '2', // 2 second segments + '-window_size', '2', // two chunks in the list at a time + '-streaming', '1', // fragment data + '-dash_segment_type', 'webm', // container type + '/outbound/output.mpd' // path to manifest file in virtual FS, + // must be under /outbound + ], + MEMFS: [ + { name: 'stream1' }, + { name: 'stream2' } + ] +``` +* For HLS and DASH: + * `{type: "base-url", data: "", protocol: "hls|dash", options: "}` - Sets the URL to stream data to. The generated HLS or DASH chunk filenames are appended to this. Send this before sending any `stream-data`. Default method is POST for HLS and PUT for DASH. * `{type: "stream-data", name: "", data: ""}` - Data (audio/video/muxed) to supply to FFmpeg. `filename` must match an argument passed to FFmpeg via a `-i` option (up to two files are supported). `data` is an `ArrayBuffer`. * `{type: "stream-end"}` - End all input streams (FFmpeg will exit after this). + ```js const worker = new Worker("ffmpeg-worker-webm.js"); worker.onmessage = function(e) { @@ -205,9 +231,9 @@ Thanks to [videoconverter.js](https://bgrins.github.io/videoconverter.js/) for i Own library code licensed under LGPL 2.1 or later. -### WebM build +### WebM, HLS and DASH builds -This build uses LGPL version of FFmpeg and thus available under LGPL 2.1 or later. See [here](https://www.ffmpeg.org/legal.html) for more details and FFmpeg's license information. +These builds use the LGPL version of FFmpeg and are thus available under LGPL 2.1 or later. See [here](https://www.ffmpeg.org/legal.html) for more details and FFmpeg's license information. Included libraries: * libopus [licensed under BSD](https://git.xiph.org/?p=opus.git;a=blob;f=COPYING). @@ -217,7 +243,7 @@ See [LICENSE.WEBM](https://github.com/Kagami/ffmpeg.js/blob/master/LICENSE.WEBM) ### MP4 build -This build uses GPL version of FFmpeg and thus available under GPL 2.0. It also includes patent encumbered H.264, AAC and MP3 encoders. Make sure to contact lawyer before using it in your country. +This build uses GPL version of FFmpeg and is thus available under GPL 2.0. It also includes patent encumbered H.264, AAC and MP3 encoders. Make sure to contact a lawyer before using it in your country. Included libraries: * x264 [licensed under GPL](https://git.videolan.org/?p=x264.git;a=blob;f=COPYING). From 8b9ee574346044d83333f497033b68928dda6b43 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 17 Oct 2021 09:04:30 +0100 Subject: [PATCH 32/43] Update README to reflect default request method --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6cdd375..3adfc8d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Currently available builds (additional builds may be added in future): * `ffmpeg-mp4.js` - MP4 encoding (H.264 & AAC & MP3 encoders, popular decoders). * `ffmpeg-worker-mp4.js` - Web Worker version of `ffmpeg-mp4.js`. * `ffmpeg-worker-hls.js` - HLS (HTTP Live Streaming) in a Web Worker. Data is MPEG2-TS encoded and POSTed via HTTP. Note this uses Web Assembly. -* `ffmpeg-worker-dash.js` - DASH (Dynamic Adaptive Streaming over HTTP) in a Web Worker. Data is PUT or POSTED via HTTP. Note this uses Web Assembly. +* `ffmpeg-worker-dash.js` - DASH (Dynamic Adaptive Streaming over HTTP) in a Web Worker. Data is POSTed via HTTP. Note this uses Web Assembly. Note: only NPM releases contain abovementioned files. @@ -119,7 +119,7 @@ You can send the following messages to the worker: ] ``` * For HLS and DASH: - * `{type: "base-url", data: "", protocol: "hls|dash", options: "}` - Sets the URL to stream data to. The generated HLS or DASH chunk filenames are appended to this. Send this before sending any `stream-data`. Default method is POST for HLS and PUT for DASH. + * `{type: "base-url", data: "", protocol: "hls|dash", options: "}` - Sets the URL to stream data to. The generated HLS or DASH chunk filenames are appended to this. Send this before sending any `stream-data`. Default method is POST but you can change this using the request options. * `{type: "stream-data", name: "", data: ""}` - Data (audio/video/muxed) to supply to FFmpeg. `filename` must match an argument passed to FFmpeg via a `-i` option (up to two files are supported). `data` is an `ArrayBuffer`. * `{type: "stream-end"}` - End all input streams (FFmpeg will exit after this). From 602db8f92d9ca046186a734a7f40bb34d419a11a Mon Sep 17 00:00:00 2001 From: David Halls Date: Tue, 19 Oct 2021 22:41:38 +0100 Subject: [PATCH 33/43] HLS/DASH: If fetch mode isn't no-cors, log error --- build/library-hls.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/build/library-hls.js b/build/library-hls.js index fe7d819..004f5eb 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -213,18 +213,15 @@ mergeInto(LibraryManager.library, { const stream = FS.streams[fd]; if (stream && stream.upload_url) { console.log("MAKING REQUEST TO", stream.upload_url); - fetch(stream.upload_url, Object.assign({ + const options = Object.assign({ mode: 'no-cors', method: 'POST', - /* no-cors so we can't set octet-stream - headers: { - 'Content-Type': 'application/octet-stream' - }*/ body: new Blob(stream.upload_data) - }, self.upload_options)).then(response => { - if (!response.ok) { - // no-cors so response is opaque and ok will always be false - //console.error("RESPONSE NOT OK", stream.upload_url, response); + }, self.upload_options); + fetch(stream.upload_url, options).then(response => { + // note: with no-cors, response is opaque and ok will always be false + if (!response.ok && (options.mode !== 'no-cors')) { + console.error("RESPONSE NOT OK", stream.upload_url, response); } }).catch (err => { console.error("REQUEST ERROR", stream.upload_url, err); From d38348c044cdb9546a3a14ce9cc61a05cd742f0b Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 9 Dec 2021 07:41:19 +0000 Subject: [PATCH 34/43] postMessage if upload URL starts with postMessage: --- build/library-hls.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/build/library-hls.js b/build/library-hls.js index 004f5eb..a208423 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -213,19 +213,27 @@ mergeInto(LibraryManager.library, { const stream = FS.streams[fd]; if (stream && stream.upload_url) { console.log("MAKING REQUEST TO", stream.upload_url); - const options = Object.assign({ - mode: 'no-cors', - method: 'POST', - body: new Blob(stream.upload_data) - }, self.upload_options); - fetch(stream.upload_url, options).then(response => { - // note: with no-cors, response is opaque and ok will always be false - if (!response.ok && (options.mode !== 'no-cors')) { - console.error("RESPONSE NOT OK", stream.upload_url, response); - } - }).catch (err => { - console.error("REQUEST ERROR", stream.upload_url, err); - }); + if (stream.upload_url.startsWith('postMessage:')) { + self.postMessage(Object.assign({ + type: 'upload', + url: stream.upload_url, + data: stream.upload_data + }, self.upload_options), stream.upload_data); + } else { + const options = Object.assign({ + mode: 'no-cors', + method: 'POST', + body: new Blob(stream.upload_data) + }, self.upload_options); + fetch(stream.upload_url, options).then(response => { + // note: with no-cors, response is opaque and ok will always be false + if (!response.ok && (options.mode !== 'no-cors')) { + console.error("RESPONSE NOT OK", stream.upload_url, response); + } + }).catch (err => { + console.error("REQUEST ERROR", stream.upload_url, err); + }); + } } wakeUp(); }); From c99d521e64354198a2971ef732eeb46deea02f91 Mon Sep 17 00:00:00 2001 From: David Halls Date: Thu, 9 Dec 2021 23:31:25 +0000 Subject: [PATCH 35/43] postMessage: transfer readable stream --- build/library-hls.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build/library-hls.js b/build/library-hls.js index a208423..cc5ed8a 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -213,17 +213,19 @@ mergeInto(LibraryManager.library, { const stream = FS.streams[fd]; if (stream && stream.upload_url) { console.log("MAKING REQUEST TO", stream.upload_url); + const upload_data = new Blob(stream.upload_data); if (stream.upload_url.startsWith('postMessage:')) { + const upload_stream = upload_data.stream(); self.postMessage(Object.assign({ type: 'upload', url: stream.upload_url, - data: stream.upload_data - }, self.upload_options), stream.upload_data); + stream: upload_stream + }, self.upload_options), [upload_stream]); } else { const options = Object.assign({ mode: 'no-cors', method: 'POST', - body: new Blob(stream.upload_data) + body: upload_data }, self.upload_options); fetch(stream.upload_url, options).then(response => { // note: with no-cors, response is opaque and ok will always be false From 8abbe514e57f4fa161e0b612028d5ae644c5e57d Mon Sep 17 00:00:00 2001 From: David Halls Date: Fri, 10 Dec 2021 07:46:17 +0000 Subject: [PATCH 36/43] Update README for HLS/DASH postMessage --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3adfc8d..e685669 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ ffmpeg.js also provides wrapper for main function with Web Worker interface to o * `{type: "start-stream"} - FFmpeg is ready to stream data (application should start posting `stream-data` messages). * `{type: "sending"} - Sent once when data starts to be POSTed. * `{type: "ffexit", code: ""}` - FFmpeg exited with status code. + * `{type: "upload", stream: "", url: ""}` - Only if upload URL starts with `postMessage:` (see below). You can send the following messages to the worker: * `{type: "run", ...opts}` - Start new job with provided options. @@ -120,6 +121,7 @@ You can send the following messages to the worker: ``` * For HLS and DASH: * `{type: "base-url", data: "", protocol: "hls|dash", options: "}` - Sets the URL to stream data to. The generated HLS or DASH chunk filenames are appended to this. Send this before sending any `stream-data`. Default method is POST but you can change this using the request options. + * Note: If the upload URL starts with `postMessage:` then instead of streaming to the network, the Worker will `postMessage` each chunk (see above). * `{type: "stream-data", name: "", data: ""}` - Data (audio/video/muxed) to supply to FFmpeg. `filename` must match an argument passed to FFmpeg via a `-i` option (up to two files are supported). `data` is an `ArrayBuffer`. * `{type: "stream-end"}` - End all input streams (FFmpeg will exit after this). From 233c56e14b2697947a6db3b02c64f4ddabae8e6e Mon Sep 17 00:00:00 2001 From: David Halls Date: Fri, 10 Jun 2022 21:47:50 +0100 Subject: [PATCH 37/43] Update deps, fix compile with emscripten 3.1.13 --- Makefile | 1 - package-lock.json | 1903 +++++++++++++++++++++++++++++++-------------- 2 files changed, 1318 insertions(+), 586 deletions(-) diff --git a/Makefile b/Makefile index 8e9b48a..51769b7 100644 --- a/Makefile +++ b/Makefile @@ -297,7 +297,6 @@ EMCC_COMMON_CORE_ARGS = \ --memory-init-file 0 \ -s WASM_ASYNC_COMPILATION=0 \ -s ASSERTIONS=0 \ - -s EXIT_RUNTIME=1 \ -s TOTAL_MEMORY=67108864 \ --pre-js $(PRE_JS) \ -o $@ diff --git a/package-lock.json b/package-lock.json index ceff531..2dd459d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,34 +1,1042 @@ { "name": "ffmpeg.js", "version": "4.2.9003", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "ffmpeg.js", + "version": "4.2.9003", + "license": "(LGPL-2.1+ OR GPL-2.0)", + "devDependencies": { + "chai": "latest", + "mocha": "latest" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, "dependencies": { + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -36,13 +1044,10 @@ } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "assertion-error": { "version": "1.1.0", @@ -51,25 +1056,24 @@ "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "braces": { @@ -88,43 +1092,43 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.1.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", "type-detect": "^4.0.5" } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "dependencies": { "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } @@ -132,98 +1136,78 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.6.0" } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "deep-eql": { @@ -235,67 +1219,28 @@ "type-detect": "^4.0.0" } }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "fill-range": { @@ -308,42 +1253,34 @@ } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -353,13 +1290,13 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -368,42 +1305,42 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "he": { @@ -415,7 +1352,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -437,40 +1374,22 @@ "binary-extensions": "^2.0.0" } }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -482,136 +1401,105 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true - }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^2.4.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "get-func-name": "^2.0.0" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "requires": { - "minimist": "^1.2.5" + "brace-expansion": "^2.0.1" } }, "mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", + "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -619,210 +1507,129 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" } }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.2.1" } }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" + "randombytes": "^2.1.0" } }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "to-regex-range": { @@ -840,67 +1647,21 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -910,77 +1671,49 @@ "dev": true }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } From 42191f5248d91b7e7b914bbaacd4d735f9e708df Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 24 Jul 2022 08:42:25 +0100 Subject: [PATCH 38/43] Don't exit if fetch requests are pending Upper layer can always terminate worker if it needs to exit without waiting --- Makefile | 8 +++--- ...peg-exit.patch => ffmpeg-async-exit.patch} | 4 +-- build/library-hls.js | 28 +++++++++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) rename build/{ffmpeg-exit.patch => ffmpeg-async-exit.patch} (84%) diff --git a/Makefile b/Makefile index 51769b7..0b8dea9 100644 --- a/Makefile +++ b/Makefile @@ -252,7 +252,7 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) git reset --hard && \ patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-hls-configure.patch && \ - patch -p1 < ../ffmpeg-exit.patch && \ + patch -p1 < ../ffmpeg-async-exit.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_HLS_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(HLS_DEMUXERS)) \ @@ -275,7 +275,7 @@ build/ffmpeg-dash/ffmpeg.bc: patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-dash-configure.patch && \ patch -p1 < ../ffmpeg-dash-codecs.patch && \ - patch -p1 < ../ffmpeg-exit.patch && \ + patch -p1 < ../ffmpeg-async-exit.patch && \ emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(DASH_DEMUXERS)) \ @@ -335,7 +335,7 @@ ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_J --js-library $(LIBRARY_HLS_JS) \ -s WASM=1 \ -s ASYNCIFY \ - -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async"]' + -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async", "emscripten_exit_async"]' ffmpeg-worker-dash.js ffmpeg-worker-dash.wasm: $(FFMPEG_DASH_BC) $(PRE_JS) $(POST_JS_WORKER) $(LIBRARY_DASH_JS) emcc $(FFMPEG_DASH_BC) \ @@ -344,4 +344,4 @@ ffmpeg-worker-dash.js ffmpeg-worker-dash.wasm: $(FFMPEG_DASH_BC) $(PRE_JS) $(POS --js-library $(LIBRARY_DASH_JS) \ -s WASM=1 \ -s ASYNCIFY \ - -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async"]' + -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async", "emscripten_exit_async"]' diff --git a/build/ffmpeg-exit.patch b/build/ffmpeg-async-exit.patch similarity index 84% rename from build/ffmpeg-exit.patch rename to build/ffmpeg-async-exit.patch index 6fc545c..394ed85 100644 --- a/build/ffmpeg-exit.patch +++ b/build/ffmpeg-async-exit.patch @@ -6,7 +6,7 @@ index fe424b6a4c..c9a8a7cb29 100644 program_exit = cb; } -+int emscripten_exit(int code); ++int emscripten_exit_async(int code); + void exit_program(int ret) { @@ -14,7 +14,7 @@ index fe424b6a4c..c9a8a7cb29 100644 program_exit(ret); - exit(ret); -+ exit(emscripten_exit(ret)); ++ exit(emscripten_exit_async(ret)); } double parse_number_or_die(const char *context, const char *numstr, int type, diff --git a/build/library-hls.js b/build/library-hls.js index cc5ed8a..c201066 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -227,12 +227,24 @@ mergeInto(LibraryManager.library, { method: 'POST', body: upload_data }, self.upload_options); + self.pending_fetches = (self.pending_fetches || 0) + 1; + function check_exit() { + if ((--self.pending_fetches === 0) && + (self.pending_exit_code !== undefined)) { + self.postMessage({ + type: 'ffexit', + code: self.pending_exit_code + }); + } + } fetch(stream.upload_url, options).then(response => { + check_exit(); // note: with no-cors, response is opaque and ok will always be false if (!response.ok && (options.mode !== 'no-cors')) { console.error("RESPONSE NOT OK", stream.upload_url, response); } }).catch (err => { + check_exit(); console.error("REQUEST ERROR", stream.upload_url, err); }); } @@ -240,11 +252,17 @@ mergeInto(LibraryManager.library, { wakeUp(); }); }, - emscripten_exit: function (code) { - self.postMessage({ - type: 'ffexit', - code + emscripten_exit_async: function (code) { + return Asyncify.handleSleep(wakeUp => { + if (self.pending_fetches > 0) { + console.log(`EXIT with ${self.pending_fetches} pending fetches`); + self.pending_exit_code = code; + } else { + self.postMessage({ + type: 'ffexit', + code + }); + } }); - return code; } }); From 7257e615603cce84e49501d2654fbbb77af53854 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sun, 24 Jul 2022 10:41:21 +0100 Subject: [PATCH 39/43] Update ffmpeg to n5.1 Also update other submodules that have newer versions available --- build/ffmpeg-async-exit.patch | 2 +- build/ffmpeg-async-io.patch | 9 +++++---- build/ffmpeg-dash | 2 +- build/ffmpeg-dash-codecs.patch | 4 ++-- build/ffmpeg-dash-configure.patch | 4 ++-- build/ffmpeg-hls | 2 +- build/ffmpeg-hls-configure.patch | 4 ++-- build/ffmpeg-mp4 | 2 +- build/ffmpeg-webm | 2 +- build/libvpx | 2 +- build/libvpx-fix-ld.patch | 2 +- build/x264 | 2 +- 12 files changed, 19 insertions(+), 18 deletions(-) diff --git a/build/ffmpeg-async-exit.patch b/build/ffmpeg-async-exit.patch index 394ed85..6325fb8 100644 --- a/build/ffmpeg-async-exit.patch +++ b/build/ffmpeg-async-exit.patch @@ -2,7 +2,7 @@ diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index fe424b6a4c..c9a8a7cb29 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c -@@ -130,12 +130,14 @@ void register_exit(void (*cb)(int ret)) +@@ -90,12 +90,14 @@ void register_exit(void (*cb)(int ret)) program_exit = cb; } diff --git a/build/ffmpeg-async-io.patch b/build/ffmpeg-async-io.patch index dfd9742..12fa9bf 100644 --- a/build/ffmpeg-async-io.patch +++ b/build/ffmpeg-async-io.patch @@ -2,7 +2,7 @@ diff --git a/libavformat/file.c b/libavformat/file.c index 8303436be0..4aa206859e 100644 --- a/libavformat/file.c +++ b/libavformat/file.c -@@ -38,6 +38,9 @@ +@@ -40,6 +40,9 @@ #include "os_support.h" #include "url.h" @@ -12,7 +12,7 @@ index 8303436be0..4aa206859e 100644 /* Some systems may not have S_ISFIFO */ #ifndef S_ISFIFO # ifdef S_IFIFO -@@ -111,7 +114,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) +@@ -113,7 +116,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) FileContext *c = h->priv_data; int ret; size = FFMIN(size, c->blocksize); @@ -25,11 +25,12 @@ index 8303436be0..4aa206859e 100644 if (ret == 0 && c->follow) return AVERROR(EAGAIN); if (ret == 0) -@@ -266,6 +273,7 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence) +@@ -264,7 +271,8 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence) static int file_close(URLContext *h) { FileContext *c = h->priv_data; + emscripten_close_async(c->fd); - return close(c->fd); + int ret = close(c->fd); + return (ret == -1) ? AVERROR(errno) : 0; } diff --git a/build/ffmpeg-dash b/build/ffmpeg-dash index dc91b91..e0723b7 160000 --- a/build/ffmpeg-dash +++ b/build/ffmpeg-dash @@ -1 +1 @@ -Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 +Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b diff --git a/build/ffmpeg-dash-codecs.patch b/build/ffmpeg-dash-codecs.patch index a958bfa..9074249 100644 --- a/build/ffmpeg-dash-codecs.patch +++ b/build/ffmpeg-dash-codecs.patch @@ -2,7 +2,7 @@ diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 06bbf36c95..8ed9cd3a47 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c -@@ -375,83 +375,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, +@@ -365,83 +365,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, } return; } @@ -90,7 +90,7 @@ diff --git a/libavformat/Makefile b/libavformat/Makefile index c45caa3eed..d8d39d78bf 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile -@@ -153,7 +153,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o +@@ -164,7 +164,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER) += rawenc.o diff --git a/build/ffmpeg-dash-configure.patch b/build/ffmpeg-dash-configure.patch index e435984..9bc010c 100644 --- a/build/ffmpeg-dash-configure.patch +++ b/build/ffmpeg-dash-configure.patch @@ -2,8 +2,8 @@ diff --git a/configure b/configure index 92610c7edc..d30a5284c0 100755 --- a/configure +++ b/configure -@@ -3313,7 +3313,6 @@ avi_demuxer_select="riffdec exif" - avi_muxer_select="riffenc" +@@ -3412,7 +3412,6 @@ avi_demuxer_select="riffdec exif" + avif_muxer_select="mov_muxer" caf_demuxer_select="iso_media" caf_muxer_select="iso_media" -dash_muxer_select="mp4_muxer" diff --git a/build/ffmpeg-hls b/build/ffmpeg-hls index dc91b91..e0723b7 160000 --- a/build/ffmpeg-hls +++ b/build/ffmpeg-hls @@ -1 +1 @@ -Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 +Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b diff --git a/build/ffmpeg-hls-configure.patch b/build/ffmpeg-hls-configure.patch index 689a46e..66c37fc 100644 --- a/build/ffmpeg-hls-configure.patch +++ b/build/ffmpeg-hls-configure.patch @@ -2,12 +2,12 @@ diff --git a/configure b/configure index d7a3f507e8..746f130a0a 100755 --- a/configure +++ b/configure -@@ -3349,7 +3349,7 @@ mp3_demuxer_select="mpegaudio_parser" +@@ -3451,7 +3451,7 @@ mp3_demuxer_select="mpegaudio_parser" mp3_muxer_select="mpegaudioheader" mp4_muxer_select="mov_muxer" mpegts_demuxer_select="iso_media" -mpegts_muxer_select="ac3_parser adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf" +mpegts_muxer_select="adts_muxer" mpegtsraw_demuxer_select="mpegts_demuxer" - mxf_muxer_select="golomb pcm_rechunk_bsf" + mxf_muxer_select="pcm_rechunk_bsf" mxf_d10_muxer_select="mxf_muxer" diff --git a/build/ffmpeg-mp4 b/build/ffmpeg-mp4 index dc91b91..e0723b7 160000 --- a/build/ffmpeg-mp4 +++ b/build/ffmpeg-mp4 @@ -1 +1 @@ -Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 +Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b diff --git a/build/ffmpeg-webm b/build/ffmpeg-webm index dc91b91..e0723b7 160000 --- a/build/ffmpeg-webm +++ b/build/ffmpeg-webm @@ -1 +1 @@ -Subproject commit dc91b913b6260e85e1304c74ff7bb3c22a8c9fb1 +Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b diff --git a/build/libvpx b/build/libvpx index b41ffb5..03265cd 160000 --- a/build/libvpx +++ b/build/libvpx @@ -1 +1 @@ -Subproject commit b41ffb53f1000ab2227c1736d8c1355aa5081c40 +Subproject commit 03265cd42b3783532de72f2ded5436652e6f5ce3 diff --git a/build/libvpx-fix-ld.patch b/build/libvpx-fix-ld.patch index 176c0f9..e1cda57 100644 --- a/build/libvpx-fix-ld.patch +++ b/build/libvpx-fix-ld.patch @@ -2,7 +2,7 @@ diff --git a/build/make/Makefile b/build/make/Makefile index be2810229..5a622c05d 100644 --- a/build/make/Makefile +++ b/build/make/Makefile -@@ -303,8 +303,8 @@ define so_template +@@ -313,8 +313,8 @@ define so_template $(1): $(if $(quiet),@echo " [LD] $$@") $(qexec)$$(LD) -shared $$(LDFLAGS) \ diff --git a/build/x264 b/build/x264 index 296494a..baee400 160000 --- a/build/x264 +++ b/build/x264 @@ -1 +1 @@ -Subproject commit 296494a4011f58f32adc54304a2654627558c59a +Subproject commit baee400fa9ced6f5481a728138fed6e867b0ff7f From d1b1559d29fa6851f5dace690e85a5858fe51bc5 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 13 May 2023 21:51:31 +0100 Subject: [PATCH 40/43] Update ffmpeg to n6.0 and other submodules --- build/ffmpeg-dash | 2 +- build/ffmpeg-hls | 2 +- build/ffmpeg-mp4 | 2 +- build/ffmpeg-webm | 2 +- build/libvpx | 2 +- build/opus | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/ffmpeg-dash b/build/ffmpeg-dash index e0723b7..ea3d24b 160000 --- a/build/ffmpeg-dash +++ b/build/ffmpeg-dash @@ -1 +1 @@ -Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b +Subproject commit ea3d24bbe3c58b171e55fe2151fc7ffaca3ab3d2 diff --git a/build/ffmpeg-hls b/build/ffmpeg-hls index e0723b7..ea3d24b 160000 --- a/build/ffmpeg-hls +++ b/build/ffmpeg-hls @@ -1 +1 @@ -Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b +Subproject commit ea3d24bbe3c58b171e55fe2151fc7ffaca3ab3d2 diff --git a/build/ffmpeg-mp4 b/build/ffmpeg-mp4 index e0723b7..ea3d24b 160000 --- a/build/ffmpeg-mp4 +++ b/build/ffmpeg-mp4 @@ -1 +1 @@ -Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b +Subproject commit ea3d24bbe3c58b171e55fe2151fc7ffaca3ab3d2 diff --git a/build/ffmpeg-webm b/build/ffmpeg-webm index e0723b7..ea3d24b 160000 --- a/build/ffmpeg-webm +++ b/build/ffmpeg-webm @@ -1 +1 @@ -Subproject commit e0723b7e4e22492275d476fcd30d759e1198bc5b +Subproject commit ea3d24bbe3c58b171e55fe2151fc7ffaca3ab3d2 diff --git a/build/libvpx b/build/libvpx index 03265cd..d6eb969 160000 --- a/build/libvpx +++ b/build/libvpx @@ -1 +1 @@ -Subproject commit 03265cd42b3783532de72f2ded5436652e6f5ce3 +Subproject commit d6eb9696aa72473c1a11d34d928d35a3acc0c9a9 diff --git a/build/opus b/build/opus index e85ed77..82ac57d 160000 --- a/build/opus +++ b/build/opus @@ -1 +1 @@ -Subproject commit e85ed7726db5d677c9c0677298ea0cb9c65bdd23 +Subproject commit 82ac57d9f1aaf575800cf17373348e45b7ce6c0d From 59767be97cebb7118e3d3981bdd8738a3091be15 Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 17 May 2023 05:58:01 +0100 Subject: [PATCH 41/43] Update patches to apply cleanly --- build/ffmpeg-async-exit.patch | 6 +++--- build/ffmpeg-async-io.patch | 9 ++++----- build/ffmpeg-dash-codecs.patch | 8 ++++---- build/ffmpeg-dash-configure.patch | 4 ++-- build/ffmpeg-hls-configure.patch | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/build/ffmpeg-async-exit.patch b/build/ffmpeg-async-exit.patch index 6325fb8..fb4946a 100644 --- a/build/ffmpeg-async-exit.patch +++ b/build/ffmpeg-async-exit.patch @@ -1,9 +1,9 @@ diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c -index fe424b6a4c..c9a8a7cb29 100644 +index a1de621d1c..88bbf4cc79 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c -@@ -90,12 +90,14 @@ void register_exit(void (*cb)(int ret)) - program_exit = cb; +@@ -96,12 +96,14 @@ void report_and_exit(int ret) + exit_program(AVUNERROR(ret)); } +int emscripten_exit_async(int code); diff --git a/build/ffmpeg-async-io.patch b/build/ffmpeg-async-io.patch index 12fa9bf..5cb10c9 100644 --- a/build/ffmpeg-async-io.patch +++ b/build/ffmpeg-async-io.patch @@ -1,8 +1,8 @@ diff --git a/libavformat/file.c b/libavformat/file.c -index 8303436be0..4aa206859e 100644 +index cbdf48de0a..4e833978d1 100644 --- a/libavformat/file.c +++ b/libavformat/file.c -@@ -40,6 +40,9 @@ +@@ -41,6 +41,9 @@ #include "os_support.h" #include "url.h" @@ -12,7 +12,7 @@ index 8303436be0..4aa206859e 100644 /* Some systems may not have S_ISFIFO */ #ifndef S_ISFIFO # ifdef S_IFIFO -@@ -113,7 +116,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) +@@ -140,7 +143,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) FileContext *c = h->priv_data; int ret; size = FFMIN(size, c->blocksize); @@ -25,7 +25,7 @@ index 8303436be0..4aa206859e 100644 if (ret == 0 && c->follow) return AVERROR(EAGAIN); if (ret == 0) -@@ -264,7 +271,8 @@ static int64_t file_seek(URLContext *h, int64_t pos, int whence) +@@ -218,6 +225,7 @@ static int fd_dup(URLContext *h, int oldfd) static int file_close(URLContext *h) { FileContext *c = h->priv_data; @@ -33,4 +33,3 @@ index 8303436be0..4aa206859e 100644 int ret = close(c->fd); return (ret == -1) ? AVERROR(errno) : 0; } - diff --git a/build/ffmpeg-dash-codecs.patch b/build/ffmpeg-dash-codecs.patch index 9074249..8319d5d 100644 --- a/build/ffmpeg-dash-codecs.patch +++ b/build/ffmpeg-dash-codecs.patch @@ -1,8 +1,8 @@ diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c -index 06bbf36c95..8ed9cd3a47 100644 +index 17fe5f430c..dbbd504ef4 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c -@@ -365,83 +365,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, +@@ -368,83 +368,6 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, } return; } @@ -87,10 +87,10 @@ index 06bbf36c95..8ed9cd3a47 100644 static int flush_dynbuf(DASHContext *c, OutputStream *os, int *range_length) diff --git a/libavformat/Makefile b/libavformat/Makefile -index c45caa3eed..d8d39d78bf 100644 +index 47bbbbfb2a..cafa1879bd 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile -@@ -164,7 +164,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o +@@ -167,7 +167,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER) += rawenc.o diff --git a/build/ffmpeg-dash-configure.patch b/build/ffmpeg-dash-configure.patch index 9bc010c..9f63373 100644 --- a/build/ffmpeg-dash-configure.patch +++ b/build/ffmpeg-dash-configure.patch @@ -1,8 +1,8 @@ diff --git a/configure b/configure -index 92610c7edc..d30a5284c0 100755 +index b6616f00b6..d102f263f6 100755 --- a/configure +++ b/configure -@@ -3412,7 +3412,6 @@ avi_demuxer_select="riffdec exif" +@@ -3421,7 +3421,6 @@ avi_muxer_select="riffenc" avif_muxer_select="mov_muxer" caf_demuxer_select="iso_media" caf_muxer_select="iso_media" diff --git a/build/ffmpeg-hls-configure.patch b/build/ffmpeg-hls-configure.patch index 66c37fc..d8d6295 100644 --- a/build/ffmpeg-hls-configure.patch +++ b/build/ffmpeg-hls-configure.patch @@ -1,8 +1,8 @@ diff --git a/configure b/configure -index d7a3f507e8..746f130a0a 100755 +index b6616f00b6..ce733aa2e7 100755 --- a/configure +++ b/configure -@@ -3451,7 +3451,7 @@ mp3_demuxer_select="mpegaudio_parser" +@@ -3460,7 +3460,7 @@ mp3_demuxer_select="mpegaudio_parser" mp3_muxer_select="mpegaudioheader" mp4_muxer_select="mov_muxer" mpegts_demuxer_select="iso_media" From 1b61fc614b41395721f9ffccb5a2f6ac4db34dd4 Mon Sep 17 00:00:00 2001 From: David Halls Date: Wed, 17 May 2023 05:58:22 +0100 Subject: [PATCH 42/43] ffmpeg needs pthreads now --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 0b8dea9..c3890b1 100644 --- a/Makefile +++ b/Makefile @@ -185,7 +185,6 @@ FFMPEG_COMMON_CORE_ARGS = \ --disable-runtime-cpudetect \ --disable-asm \ --disable-fast-unaligned \ - --disable-pthreads \ --disable-w32threads \ --disable-os2threads \ --disable-debug \ From 7f3fe0e206a6aade3307d3069e25b80986bf5c97 Mon Sep 17 00:00:00 2001 From: David Halls Date: Sat, 4 Nov 2023 22:20:25 +0000 Subject: [PATCH 43/43] Fixes for threading --- Makefile | 13 +- build/ffmpeg-async-io.patch | 15 +- build/ffmpeg-hls-configure.patch | 11 +- build/ffmpeg-pthread-exit.patch | 13 ++ build/library-hls.js | 216 +++--------------- build/post-worker.js | 368 +++++++++++++++++++++++++++---- build/pre.js | 230 +++++++++---------- 7 files changed, 508 insertions(+), 358 deletions(-) create mode 100644 build/ffmpeg-pthread-exit.patch diff --git a/Makefile b/Makefile index c3890b1..33aff8e 100644 --- a/Makefile +++ b/Makefile @@ -192,6 +192,7 @@ FFMPEG_COMMON_CORE_ARGS = \ --disable-safe-bitstream-reader \ \ --disable-all \ + --enable-pthreads \ --enable-ffmpeg \ --enable-avcodec \ --enable-avformat \ @@ -252,6 +253,7 @@ build/ffmpeg-hls/ffmpeg.bc: $(HLS_SHARED_DEPS) patch -p1 < ../ffmpeg-async-io.patch && \ patch -p1 < ../ffmpeg-hls-configure.patch && \ patch -p1 < ../ffmpeg-async-exit.patch && \ + patch -p1 < ../ffmpeg-pthread-exit.patch && \ EM_PKG_CONFIG_PATH=$(FFMPEG_HLS_PC_PATH) emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(HLS_DEMUXERS)) \ @@ -275,6 +277,7 @@ build/ffmpeg-dash/ffmpeg.bc: patch -p1 < ../ffmpeg-dash-configure.patch && \ patch -p1 < ../ffmpeg-dash-codecs.patch && \ patch -p1 < ../ffmpeg-async-exit.patch && \ + patch -p1 < ../ffmpeg-pthread-exit.patch && \ emconfigure ./configure \ $(FFMPEG_COMMON_CORE_ARGS) \ $(addprefix --enable-demuxer=,$(DASH_DEMUXERS)) \ @@ -296,7 +299,11 @@ EMCC_COMMON_CORE_ARGS = \ --memory-init-file 0 \ -s WASM_ASYNC_COMPILATION=0 \ -s ASSERTIONS=0 \ - -s TOTAL_MEMORY=67108864 \ + -s TOTAL_MEMORY=100MB \ + -s STACK_SIZE=5MB \ + -s DEFAULT_PTHREAD_STACK_SIZE=5MB \ + -s PTHREAD_POOL_SIZE=10 \ + -s ASYNCIFY_STACK_SIZE=65536 \ --pre-js $(PRE_JS) \ -o $@ @@ -331,7 +338,9 @@ ffmpeg-worker-hls.js ffmpeg-worker-hls.wasm: $(FFMPEG_HLS_BC) $(PRE_JS) $(POST_J emcc $(FFMPEG_HLS_BC) $(HLS_SHARED_DEPS) \ --post-js $(POST_JS_WORKER) \ $(EMCC_COMMON_CORE_ARGS) \ + -pthread \ --js-library $(LIBRARY_HLS_JS) \ + -s PROXY_TO_PTHREAD \ -s WASM=1 \ -s ASYNCIFY \ -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async", "emscripten_exit_async"]' @@ -340,7 +349,9 @@ ffmpeg-worker-dash.js ffmpeg-worker-dash.wasm: $(FFMPEG_DASH_BC) $(PRE_JS) $(POS emcc $(FFMPEG_DASH_BC) \ --post-js $(POST_JS_WORKER) \ $(EMCC_COMMON_CORE_ARGS) \ + -pthread \ --js-library $(LIBRARY_DASH_JS) \ + -s PROXY_TO_PTHREAD \ -s WASM=1 \ -s ASYNCIFY \ -s 'ASYNCIFY_IMPORTS=["emscripten_read_async", "emscripten_close_async", "emscripten_exit_async"]' diff --git a/build/ffmpeg-async-io.patch b/build/ffmpeg-async-io.patch index 5cb10c9..8024f2a 100644 --- a/build/ffmpeg-async-io.patch +++ b/build/ffmpeg-async-io.patch @@ -1,18 +1,17 @@ diff --git a/libavformat/file.c b/libavformat/file.c -index cbdf48de0a..4e833978d1 100644 +index cbdf48de0a..1e53e7f647 100644 --- a/libavformat/file.c +++ b/libavformat/file.c -@@ -41,6 +41,9 @@ +@@ -41,6 +41,8 @@ #include "os_support.h" #include "url.h" +extern int emscripten_read_async(int fd, unsigned char* buf, int size); -+extern void emscripten_close_async(int fd); + /* Some systems may not have S_ISFIFO */ #ifndef S_ISFIFO # ifdef S_IFIFO -@@ -140,7 +143,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) +@@ -140,7 +142,11 @@ static int file_read(URLContext *h, unsigned char *buf, int size) FileContext *c = h->priv_data; int ret; size = FFMIN(size, c->blocksize); @@ -25,11 +24,3 @@ index cbdf48de0a..4e833978d1 100644 if (ret == 0 && c->follow) return AVERROR(EAGAIN); if (ret == 0) -@@ -218,6 +225,7 @@ static int fd_dup(URLContext *h, int oldfd) - static int file_close(URLContext *h) - { - FileContext *c = h->priv_data; -+ emscripten_close_async(c->fd); - int ret = close(c->fd); - return (ret == -1) ? AVERROR(errno) : 0; - } diff --git a/build/ffmpeg-hls-configure.patch b/build/ffmpeg-hls-configure.patch index d8d6295..5fa94f9 100644 --- a/build/ffmpeg-hls-configure.patch +++ b/build/ffmpeg-hls-configure.patch @@ -1,7 +1,16 @@ diff --git a/configure b/configure -index b6616f00b6..ce733aa2e7 100755 +index b6616f00b6..fce07721d0 100755 --- a/configure +++ b/configure +@@ -3437,7 +3437,7 @@ flv_muxer_select="aac_adtstoasc_bsf" + gxf_muxer_select="pcm_rechunk_bsf" + hds_muxer_select="flv_muxer" + hls_demuxer_select="adts_header ac3_parser mov_demuxer mpegts_demuxer" +-hls_muxer_select="mov_muxer mpegts_muxer" ++hls_muxer_select="mpegts_muxer" + hls_muxer_suggest="gcrypt openssl" + image2_alias_pix_demuxer_select="image2_demuxer" + image2_brender_pix_demuxer_select="image2_demuxer" @@ -3460,7 +3460,7 @@ mp3_demuxer_select="mpegaudio_parser" mp3_muxer_select="mpegaudioheader" mp4_muxer_select="mov_muxer" diff --git a/build/ffmpeg-pthread-exit.patch b/build/ffmpeg-pthread-exit.patch new file mode 100644 index 0000000..419ce85 --- /dev/null +++ b/build/ffmpeg-pthread-exit.patch @@ -0,0 +1,13 @@ +diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c +index ffece60720..e0af9747af 100644 +--- a/fftools/ffmpeg_demux.c ++++ b/fftools/ffmpeg_demux.c +@@ -341,6 +341,8 @@ finish: + + av_log(NULL, AV_LOG_VERBOSE, "Terminating demuxer thread %d\n", f->index); + ++ pthread_exit(NULL); ++ + return NULL; + } + diff --git a/build/library-hls.js b/build/library-hls.js index c201066..5b2107a 100644 --- a/build/library-hls.js +++ b/build/library-hls.js @@ -1,131 +1,8 @@ mergeInto(LibraryManager.library, { emscripten_read_async: function (fd, buf, size) { if (!self.stream_started) { - FS.mkdir('/outbound'); - const check_access = { - get: function (target, name, receiver) { - const r = Reflect.get(target, name, receiver); - if (r === undefined) { - console.warn('Accessed missing property:', name, target); - } - return r; - } - }; - const files = new Set(); - const ops = new Proxy({ - mount: function (mount) { - return ops.createNode(null, '/', ops.getMode('/')); - }, - createNode: function (parent, name, mode, dev) { - const node = FS.createNode(parent, name, mode, dev); - node.node_ops = ops.node_ops; - node.stream_ops = ops.stream_ops; - node.usedBytes = 0; - return node; - }, - getMode: function (path) { - return (path === '/' ? 0x40000 : 0x100000) | 0x777; - }, - realPath: function (node) { - const parts = []; - while (node.parent !== node) { - parts.push(node.name); - node = node.parent; - } - parts.reverse(); - return PATH.join.apply(null, parts); - }, - node_ops: new Proxy({ - getattr: function (node) { - const attr = {}; - attr.dev = 1; - attr.ino = node.id; - attr.nlink = 1; - attr.uid = 0; - attr.gid = 0; - attr.rdev = node.rdev; - attr.size = FS.isDir(node.mode) ? 4096 : node.usedBytes; - attr.atime = new Date(node.timestamp); - attr.mtime = new Date(node.timestamp); - attr.ctime = new Date(node.timestamp); - attr.blksize = 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - }, - setattr: function (node, attr) { - }, - lookup: function (parent, name) { - if (!files.has(name)) { - throw FS.genericErrors[{{{ cDefine('ENOENT') }}}]; - } - const path = PATH.join2(ops.realPath(parent), name); - const mode = ops.getMode(path); - return ops.createNode(parent, name, mode); - }, - mknod: function (parent, name, mode, dev) { - files.add(name); - return ops.createNode(parent, name, mode, dev); - }, - rename: function (old_node, new_dir, new_name) { - files.delete(old_node.name); - old_node.parent.timestamp = Date.now(); - old_node.name = new_name; - }, - unlink: function (parent, name) { - files.delete(name); - } - }, check_access), - stream_ops: new Proxy({ - open: function (stream) { - stream.upload_url = self.upload_url(stream.node.name); - stream.upload_data = []; - }, - llseek: function (stream, offset, whence) { - let position = offset; - if (whence === {{{ cDefine('SEEK_CUR') }}}) { - position += stream.position; - } else if (whence === {{{ cDefine('SEEK_END') }}}) { - if (FS.isFile(stream.node.mode)) { - position += stream.node.usedBytes; - } - } - if (position < 0) { - throw new FS.ErrnoError({{{ cDefine('EINVAL') }}}); - } - return position; - }, - write: function (stream, buffer, offset, length, position, canOwn) { - if (!length) { - return 0; - } - const node = stream.node; - node.timestamp = Date.now(); - node.usedBytes = Math.max(node.usedBytes, position + length); - if (stream.upload_url) { - if (!self.stream_sending) { - self.stream_sending = true; - self.postMessage({type: 'sending'}); - } -#if ALLOW_MEMORY_GROWTH - if (buffer.buffer === HEAP8.buffer) { - canOwn = false; - } -#endif - if (canOwn) { - stream.upload_data.push(buffer.subarray(offset, offset + length)); - } else { - stream.upload_data.push(buffer.slice(offset, offset + length)); - } - } - return length; - }, - close: function (stream) { - files.delete(stream.node.name); - } - }, check_access) - }, check_access); - FS.mount(ops, {}, '/outbound'); const onmessage = self.onmessage; + self.stream_is_main = true; self.stream_ended = false; self.stream_sending = false; self.stream_queues = new Map(); @@ -155,13 +32,20 @@ mergeInto(LibraryManager.library, { self.stream_handlers.delete(name); self.stream_bufs.delete(name); self.stream_sizes.delete(name); - handler(processed); + setTimeout(() => { + try { + handler(processed); + } catch (ex) { + console.error(ex); + } + }, 0); } }; self.onmessage = function (e) { const msg = e['data']; switch (msg['type']) { case 'stream-data': + self.stream_is_main = msg['is_main']; if (!self.stream_queues.has(msg['name'])) { self.stream_queues.set(msg['name'], []); } @@ -170,19 +54,6 @@ mergeInto(LibraryManager.library, { self.stream_process(msg['name']); } break; - case 'base-url': { - self.upload_url = function (name) { - if (name.endsWith('.webm') || - name.endsWith('.m4s') || - name.endsWith('.ts') || - name.endsWith('.tmp')) { - return msg['data'] + name.replace(/\.tmp$/, '').replace(/\.m4s$/, '.mp4'); - } - return null; - }; - self.upload_options = msg['options']; - break; - } case 'stream-end': self.stream_ended = true; for (let h of self.stream_handlers.keys()) { @@ -194,64 +65,35 @@ mergeInto(LibraryManager.library, { break; } }; - self.postMessage({type: 'start-stream'}); + self.postMessage({'type': 'start-stream'}); self.stream_started = true; } return Asyncify.handleSleep(wakeUp => { if (size <= 0) { - return wakeUp(0); + return setTimeout(() => { + try { + wakeUp(0); + } catch (ex) { + console.error(ex); + } + }, 0); + } + const stream = FS.streams[fd]; + let name; + if (stream) { + name = stream.node.name; + } else { + name = `/${fd}`; + if (self.stream_is_main && !self.stream_ended) { + self.postMessage({'type': 'read', 'fd': fd, 'size': size}); + } } - const name = FS.streams[fd].node.name; self.stream_handlers.set(name, wakeUp); self.stream_bufs.set(name, buf); self.stream_sizes.set(name, size); self.stream_process(name); }); }, - emscripten_close_async: function (fd) { - return Asyncify.handleSleep(wakeUp => { - const stream = FS.streams[fd]; - if (stream && stream.upload_url) { - console.log("MAKING REQUEST TO", stream.upload_url); - const upload_data = new Blob(stream.upload_data); - if (stream.upload_url.startsWith('postMessage:')) { - const upload_stream = upload_data.stream(); - self.postMessage(Object.assign({ - type: 'upload', - url: stream.upload_url, - stream: upload_stream - }, self.upload_options), [upload_stream]); - } else { - const options = Object.assign({ - mode: 'no-cors', - method: 'POST', - body: upload_data - }, self.upload_options); - self.pending_fetches = (self.pending_fetches || 0) + 1; - function check_exit() { - if ((--self.pending_fetches === 0) && - (self.pending_exit_code !== undefined)) { - self.postMessage({ - type: 'ffexit', - code: self.pending_exit_code - }); - } - } - fetch(stream.upload_url, options).then(response => { - check_exit(); - // note: with no-cors, response is opaque and ok will always be false - if (!response.ok && (options.mode !== 'no-cors')) { - console.error("RESPONSE NOT OK", stream.upload_url, response); - } - }).catch (err => { - check_exit(); - console.error("REQUEST ERROR", stream.upload_url, err); - }); - } - } - wakeUp(); - }); - }, emscripten_exit_async: function (code) { return Asyncify.handleSleep(wakeUp => { if (self.pending_fetches > 0) { @@ -259,8 +101,8 @@ mergeInto(LibraryManager.library, { self.pending_exit_code = code; } else { self.postMessage({ - type: 'ffexit', - code + 'type': 'ffexit', + 'code': code }); } }); diff --git a/build/post-worker.js b/build/post-worker.js index 2a70051..14b5ed8 100644 --- a/build/post-worker.js +++ b/build/post-worker.js @@ -1,51 +1,335 @@ - return __ffmpegjs_return; + return { PThread, FS, PATH }; } -var __ffmpegjs_running = false; - // Shim for nodejs if (typeof self === "undefined") { - self = require("worker_threads")["parentPort"]; + self = require("worker_threads")["parentPort"]; } -self.onmessage = function(e) { - var msg = e.data; - if (msg["type"] == "run") { - if (__ffmpegjs_running) { - self.postMessage({"type": "error", "data": "already running"}); - } else { - __ffmpegjs_running = true; - self.postMessage({"type": "run"}); - var opts = {}; - Object.keys(msg).forEach(function(key) { - if (key !== "type") { - opts[key] = msg[key] +function setup_outbound(FS, PATH) { + FS.mkdir('/outbound'); + const check_access = { + get: function (target, name, receiver) { + const r = Reflect.get(target, name, receiver); + if (r === undefined) { + console.warn('Accessed missing property:', name, target); + } + return r; + } + }; + const files = new Set(); + const ops = new Proxy({ + mount: function (mount) { + return ops.createNode(null, '/', ops.getMode('/')); + }, + createNode: function (parent, name, mode, dev) { + const node = FS.createNode(parent, name, mode, dev); + node.node_ops = ops.node_ops; + node.stream_ops = ops.stream_ops; + node.usedBytes = 0; + return node; + }, + getMode: function (path) { + return (path === '/' ? 0x40000 : 0x100000) | 0x777; + }, + realPath: function (node) { + const parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.reverse(); + return PATH.join.apply(null, parts); + }, + node_ops: new Proxy({ + getattr: function (node) { + const attr = {}; + attr.dev = 1; + attr.ino = node.id; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + attr.size = FS.isDir(node.mode) ? 4096 : node.usedBytes; + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr: function (node, attr) { + }, + lookup: function (parent, name) { + if (!files.has(name)) { + throw FS.genericErrors[2/*ENOENT*/]; + } + const path = PATH.join2(ops.realPath(parent), name); + const mode = ops.getMode(path); + return ops.createNode(parent, name, mode); + }, + mknod: function (parent, name, mode, dev) { + files.add(name); + return ops.createNode(parent, name, mode, dev); + }, + rename: function (old_node, new_dir, new_name) { + files.delete(old_node.name); + old_node.parent.timestamp = Date.now(); + old_node.name = new_name; + }, + unlink: function (parent, name) { + files.delete(name); + } + }, check_access), + stream_ops: new Proxy({ + open: function (stream) { + stream.upload_url = self.upload_url(stream.node.name); + stream.upload_data = []; + }, + llseek: function (stream, offset, whence) { + let position = offset; + if (whence === 1/*SEEK_CUR*/) { + position += stream.position; + } else if (whence === 2/*SEEK_END*/) { + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError(22/*EINVAL*/); + } + return position; + }, + write: function (stream, buffer, offset, length, position, canOwn) { + if (!length) { + return 0; + } + const node = stream.node; + node.timestamp = Date.now(); + node.usedBytes = Math.max(node.usedBytes, position + length); + if (stream.upload_url) { + if (!self.stream_sending) { + self.stream_sending = true; + self.postMessage({'type': 'sending'}); + } +/*#if ALLOW_MEMORY_GROWTH + if (buffer.buffer === HEAP8.buffer) { + canOwn = false; + } +#endif*/ + if (canOwn) { + stream.upload_data.push(buffer.subarray(offset, offset + length)); + } else { + stream.upload_data.push(buffer.slice(offset, offset + length)); + } + } + return length; + }, + close: function (stream) { + files.delete(stream.node.name); + if (stream.upload_url) { + console.log("MAKING REQUEST TO", stream.upload_url); + const upload_data = new Blob(stream.upload_data); + const upload_options = Object.assign({ + mode: 'no-cors', + method: 'POST' + }, self.upload_options); + if (stream.upload_url.startsWith('postMessage:')) { + upload_options.stream = upload_data.stream(); + const transfer = [upload_options.stream]; + self.postMessage({ + 'type': 'upload', + 'url': stream.upload_url, + 'options': upload_options, + 'transfer': transfer + }, transfer); + } else { + upload_options.body = upload_data; + self.pending_fetches = (self.pending_fetches || 0) + 1; + function check_exit() { + if ((--self.pending_fetches === 0) && + (self.pending_exit_code !== undefined)) { + self.postMessage({ + 'type': 'ffexit', + 'code': self.pending_exit_code + }); + } + } + fetch(stream.upload_url, upload_options).then(response => { + check_exit(); + // note: with no-cors, response is opaque and ok will always be false + if (!response.ok && (upload_options.mode !== 'no-cors')) { + console.error("RESPONSE NOT OK", stream.upload_url, response); + } + }).catch (err => { + check_exit(); + console.error("REQUEST ERROR", stream.upload_url, err); + }); + } + } + } + }, check_access) + }, check_access); + FS.mount(ops, {}, '/outbound'); +} + +if (self.onmessage) { + __ffmpegjs(); +} else { + var __ffmpegjs_running = false; + let main_worker; + const stream_workers = new Map(); + const stream_queues = new Map(); + function process_queue(name) { + const winfo = stream_workers.get(name); + const qinfo = stream_queues.get(name); + if (winfo && qinfo) { + if (winfo.worker !== main_worker) { + for (let data of qinfo.queue) { + winfo.worker.postMessage({ + 'type': 'stream-data', + 'name': winfo.fd_name, + 'data': data, + 'is_main': false + }); + } + stream_queues.delete(name); + } else if (qinfo.size >= winfo.size) { + const data = new Uint8Array(winfo.size); + let pos = 0; + while (winfo.size > 0) { + const head = qinfo.queue.shift(); + const take = Math.min(head.length, winfo.size); + data.set(head.subarray(0, take), pos); + winfo.size -= take; + qinfo.size -= take; + pos += take; + if (take < head.length) { + qinfo.queue.unshift(head.subarray(take)); + } + } + winfo.worker.postMessage({ + 'type': 'stream-data', + 'name': winfo.fd_name, + 'data': data, + 'is_main': true + }); + stream_workers.delete(name); + } } - }); - opts["print"] = function(line) { - self.postMessage({"type": "stdout", "data": line}); - }; - opts["printErr"] = function(line) { - self.postMessage({"type": "stderr", "data": line}); - }; - opts["onExit"] = function(code) { - self.postMessage({"type": "exit", "data": code}); - }; - opts["onAbort"] = function(reason) { - self.postMessage({"type": "abort", "data": reason}); - }; - // TODO(Kagami): Should we wrap this function into try/catch in - // case of possible exception? - var result = __ffmpegjs(opts); - var transfer = result["MEMFS"].map(function(file) { - return file["data"].buffer; - }); - self.postMessage({"type": "done", "data": result}, transfer); - __ffmpegjs_running = false; } - } else { - self.postMessage({"type": "error", "data": "unknown command"}); - } -}; + self.onmessage = function(e) { + var msg = e['data']; + if (msg["type"] == "run") { + if (__ffmpegjs_running) { + self.postMessage({"type": "error", "data": "already running"}); + } else { + __ffmpegjs_running = true; + self.postMessage({"type": "run"}); + var opts = {}; + Object.keys(msg).forEach(function(key) { + if (key !== "type") { + opts[key] = msg[key] + } + }); + opts["print"] = function(line) { + self.postMessage({"type": "stdout", "data": line}); + }; + opts["printErr"] = function(line) { + self.postMessage({"type": "stderr", "data": line}); + }; + opts["onExit"] = function(code) { + self.postMessage({"type": "exit", "data": code}); + }; + opts["onAbort"] = function(reason) { + self.postMessage({"type": "abort", "data": reason}); + }; + // TODO(Kagami): Should we wrap this function into try/catch in + // case of possible exception? + const { PThread, FS, PATH } = __ffmpegjs(opts, function (result) { + var transfer = result["MEMFS"].map(function(file) { + return file["data"].buffer; + }); + self.postMessage({"type": "done", "data": result}, transfer); + __ffmpegjs_running = false; + }); + setup_outbound(FS, PATH); + for (worker of PThread.unusedWorkers) { + worker.addEventListener('message', function (ev) { + const msg = ev.data; + switch (msg['type']) { + case 'read': + const name = FS.streams[msg['fd']].node.name; + stream_workers.set(name, { + worker: this, + size: msg['size'], + fd_name: `/${msg['fd']}` + }); + process_queue(name); + break; + case 'start-stream': + if (main_worker) { + break; + } + main_worker = this; + // falls through + default: + self.postMessage(msg); + } + }); + } + } + } else if (main_worker) { + switch (msg['type']) { + case 'stream-end': + for (let { worker } of stream_workers.values()) { + worker.postMessage(msg); + } + break; + + case 'stream-data': + const winfo = stream_workers.get(msg['name']); + if (!winfo || (winfo.worker === main_worker)) { + if (!stream_queues.has(msg['name'])) { + stream_queues.set(msg['name'], { queue: [], size: 0 }); + } + const qinfo = stream_queues.get(msg['name']); + qinfo.queue.push(new Uint8Array(msg['data'])); + qinfo.size += msg['data'].byteLength; + process_queue(msg['name']); + } else { + msg['name'] = winfo.fd_name; + msg['is_main'] = false; + winfo.worker.postMessage(msg); + } + break; -self.postMessage({"type": "ready"}); + case 'base-url': + self.upload_url = function (name) { + if (name.endsWith('.webm') || + name.endsWith('.m4s') || + name.endsWith('.ts') || + name.endsWith('.tmp')) { + return msg['data'] + name.replace(/\.tmp$/, '').replace(/\.m4s$/, '.mp4'); + } + return null; + }; + self.upload_options = msg['options']; + break; + + default: + main_worker.postMessage(msg); + break; + } + } else { + self.postMessage({ + "type": "error", + "data": { + "message": `unknown command : ${msg['type']}` + } + }); + } + }; + + self.postMessage({"type": "ready"}); +} diff --git a/build/pre.js b/build/pre.js index d9dabfc..1aa2853 100644 --- a/build/pre.js +++ b/build/pre.js @@ -1,129 +1,129 @@ -function __ffmpegjs(__ffmpegjs_opts) { - __ffmpegjs_opts = __ffmpegjs_opts || {}; - var __ffmpegjs_abort = abort; - var __ffmpegjs_return; - var Module = {}; +function __ffmpegjs(__ffmpegjs_opts, on_result) { + if (on_result) { + __ffmpegjs_opts = __ffmpegjs_opts || {}; + var __ffmpegjs_abort = abort; - function __ffmpegjs_toU8(data) { - if (Array.isArray(data) || data instanceof ArrayBuffer) { - data = new Uint8Array(data); - } else if (!data) { - // `null` for empty files. - data = new Uint8Array(0); - } else if (!(data instanceof Uint8Array)) { - // Avoid unnecessary copying. - data = new Uint8Array(data.buffer); + function __ffmpegjs_toU8(data) { + if (Array.isArray(data) || data instanceof ArrayBuffer) { + data = new Uint8Array(data); + } else if (!data) { + // `null` for empty files. + data = new Uint8Array(0); + } else if (!(data instanceof Uint8Array)) { + // Avoid unnecessary copying. + data = new Uint8Array(data.buffer); + } + return data; } - return data; - } - Object.keys(__ffmpegjs_opts).forEach(function(key) { - if (["mounts", "MEMFS", "onExit", "chdir"].indexOf(key) < 0) { - Module[key] = __ffmpegjs_opts[key]; - } - }); + Object.keys(__ffmpegjs_opts).forEach(function(key) { + if (["mounts", "MEMFS", "onExit", "chdir"].indexOf(key) < 0) { + Module[key] = __ffmpegjs_opts[key]; + } + }); + + // Mute exception on unreachable. + abort = function(what) { + if (arguments.length) { + __ffmpegjs_abort(what); + } else { + throw new ExitStatus(0); + } + }; - // Mute exception on unreachable. - abort = function(what) { - if (arguments.length) { - __ffmpegjs_abort(what); - } else { - throw new ExitStatus(0); + // Fix CR. + function __ffmpegjs_out(cb) { + var buf = []; + return function(ch, flush) { + if (flush && buf.length) return cb(UTF8ArrayToString(buf, 0)); + if (ch === 10 || ch === 13) { + if (ENVIRONMENT_IS_NODE) buf.push(ch); + cb(UTF8ArrayToString(buf, 0)); + buf = []; + } else if (ch !== 0) { + buf.push(ch); + } + }; + } + Module["stdin"] = Module["stdin"] || function() {}; + Module["stdout"] = Module["stdout"] || __ffmpegjs_out(function(line) { out(line) }); + Module["stderr"] = Module["stderr"] || __ffmpegjs_out(function(line) { err(line) }); + if (typeof process === "object") { + Module["print"] = Module["print"] || process.stdout.write.bind(process.stdout); + Module["printErr"] = Module["printErr"] || process.stderr.write.bind(process.stderr); } - }; - // Fix CR. - function __ffmpegjs_out(cb) { - var buf = []; - return function(ch, flush) { - if (flush && buf.length) return cb(UTF8ArrayToString(buf, 0)); - if (ch === 10 || ch === 13) { - if (ENVIRONMENT_IS_NODE) buf.push(ch); - cb(UTF8ArrayToString(buf, 0)); - buf = []; - } else if (ch !== 0) { - buf.push(ch); - } + // Disable process.exit in nodejs and don't call onExit twice. + Module["quit"] = function(status) { + Module["stdout"](0, true); + Module["stderr"](0, true); + if (__ffmpegjs_opts["onExit"]) __ffmpegjs_opts["onExit"](status); }; - } - Module["stdin"] = Module["stdin"] || function() {}; - Module["stdout"] = Module["stdout"] || __ffmpegjs_out(function(line) { out(line) }); - Module["stderr"] = Module["stderr"] || __ffmpegjs_out(function(line) { err(line) }); - if (typeof process === "object") { - Module["print"] = Module["print"] || process.stdout.write.bind(process.stdout); - Module["printErr"] = Module["printErr"] || process.stderr.write.bind(process.stderr); - } - // Disable process.exit in nodejs and don't call onExit twice. - Module["quit"] = function(status) { - Module["stdout"](0, true); - Module["stderr"](0, true); - if (__ffmpegjs_opts["onExit"]) __ffmpegjs_opts["onExit"](status); - }; + Module["preRun"] = function() { + (__ffmpegjs_opts["mounts"] || []).forEach(function(mount) { + var fs = FS.filesystems[mount["type"]]; + if (!fs) { + throw new Error("Bad mount type"); + } + var mountpoint = mount["mountpoint"]; + // NOTE(Kagami): Subdirs are not allowed in the paths to simplify + // things and avoid ".." escapes. + if (!mountpoint.match(/^\/[^\/]+$/) || + mountpoint === "/." || + mountpoint === "/.." || + mountpoint === "/tmp" || + mountpoint === "/home" || + mountpoint === "/dev" || + mountpoint === "/work") { + throw new Error("Bad mount point"); + } + FS.mkdir(mountpoint); + FS.mount(fs, mount["opts"], mountpoint); + }); - Module["preRun"] = function() { - (__ffmpegjs_opts["mounts"] || []).forEach(function(mount) { - var fs = FS.filesystems[mount["type"]]; - if (!fs) { - throw new Error("Bad mount type"); - } - var mountpoint = mount["mountpoint"]; - // NOTE(Kagami): Subdirs are not allowed in the paths to simplify - // things and avoid ".." escapes. - if (!mountpoint.match(/^\/[^\/]+$/) || - mountpoint === "/." || - mountpoint === "/.." || - mountpoint === "/tmp" || - mountpoint === "/home" || - mountpoint === "/dev" || - mountpoint === "/work") { - throw new Error("Bad mount point"); - } - FS.mkdir(mountpoint); - FS.mount(fs, mount["opts"], mountpoint); - }); + FS.mkdir("/work"); + FS.chdir(__ffmpegjs_opts["chdir"] || "/work"); - FS.mkdir("/work"); - FS.chdir(__ffmpegjs_opts["chdir"] || "/work"); + (__ffmpegjs_opts["MEMFS"] || []).forEach(function(file) { + if (file["name"].match(/\//)) { + throw new Error("Bad file name"); + } + var fd = FS.open(file["name"], "w+"); + var data = __ffmpegjs_toU8(file["data"]); + FS.write(fd, data, 0, data.length); + FS.close(fd); + }); + }; - (__ffmpegjs_opts["MEMFS"] || []).forEach(function(file) { - if (file["name"].match(/\//)) { - throw new Error("Bad file name"); + Module["postRun"] = function() { + // NOTE(Kagami): Search for files only in working directory, one + // level depth. Since FFmpeg shouldn't normally create + // subdirectories, it should be enough. + function listFiles(dir) { + var contents = FS.lookupPath(dir).node.contents; + var filenames = Object.keys(contents); + // Fix for possible file with "__proto__" name. See + // for + // details. + if (contents.__proto__ && contents.__proto__.name === "__proto__") { + filenames.push("__proto__"); + } + return filenames.map(function(filename) { + return contents[filename]; + }); } - var fd = FS.open(file["name"], "w+"); - var data = __ffmpegjs_toU8(file["data"]); - FS.write(fd, data, 0, data.length); - FS.close(fd); - }); - }; - Module["postRun"] = function() { - // NOTE(Kagami): Search for files only in working directory, one - // level depth. Since FFmpeg shouldn't normally create - // subdirectories, it should be enough. - function listFiles(dir) { - var contents = FS.lookupPath(dir).node.contents; - var filenames = Object.keys(contents); - // Fix for possible file with "__proto__" name. See - // for - // details. - if (contents.__proto__ && contents.__proto__.name === "__proto__") { - filenames.push("__proto__"); - } - return filenames.map(function(filename) { - return contents[filename]; + var inFiles = Object.create(null); + (__ffmpegjs_opts["MEMFS"] || []).forEach(function(file) { + inFiles[file.name] = null; }); - } - - var inFiles = Object.create(null); - (__ffmpegjs_opts["MEMFS"] || []).forEach(function(file) { - inFiles[file.name] = null; - }); - var outFiles = listFiles("/work").filter(function(file) { - return !(file.name in inFiles); - }).map(function(file) { - var data = __ffmpegjs_toU8(file.contents); - return {"name": file.name, "data": data}; - }); - __ffmpegjs_return = {"MEMFS": outFiles}; - }; + var outFiles = listFiles("/work").filter(function(file) { + return !(file.name in inFiles); + }).map(function(file) { + var data = __ffmpegjs_toU8(file.contents); + return {"name": file.name, "data": data}; + }); + on_result({"MEMFS": outFiles}); + }; + }