Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HLS (HTTP Live Streaming) and DASH support #166

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ffd64c9
Enable DASH
davedoesdev May 5, 2021
71112c7
Enable async stdin
davedoesdev May 5, 2021
b585b66
Create FS which will send data over HTTP
davedoesdev May 7, 2021
13cca78
DASH encoding (404 from Youtube - will require API call)
davedoesdev May 8, 2021
cca888a
async close
davedoesdev May 9, 2021
871a7f7
Add aac as common encoder
davedoesdev May 9, 2021
60a6c95
HLS working!
davedoesdev May 9, 2021
af81325
Reduce size of webm version
davedoesdev May 10, 2021
456dec2
Update ffmpeg, put h264 decoder back in
davedoesdev May 11, 2021
83535c3
Compile to WASM
davedoesdev May 12, 2021
c88fdd0
Tidy up library.js
davedoesdev May 12, 2021
c9c4f8f
Start moving hls into separate build
davedoesdev May 14, 2021
f59b285
Create separate ffmpeg-hls
davedoesdev May 15, 2021
f54ebc1
Remove more from hls version
davedoesdev May 15, 2021
b61c435
Remove mpeg2ts h264 and hevc deps
davedoesdev May 15, 2021
7e447b1
Add stream end message
davedoesdev May 21, 2021
eeddb7b
Add comments on Safari HLS support
davedoesdev May 29, 2021
376122d
Support 2 async input files (WebCodecs support)
davedoesdev Jun 6, 2021
4752a90
Add audio parsers
davedoesdev Jul 5, 2021
93053ac
Add ffexit message when exit() is called
davedoesdev Jul 16, 2021
1dd4fc0
Post message when fffmpeg starts producing data
davedoesdev Jul 30, 2021
7185433
libvpx 1.10.0
davedoesdev Oct 4, 2021
f4a9e9b
Update README for HLS
davedoesdev Oct 5, 2021
d23484b
Update Dockerfile
davedoesdev Oct 7, 2021
666ca5d
DASH live streaming support
davedoesdev Oct 14, 2021
8f0bcd7
n4.4 for ffmpeg-dash
davedoesdev Oct 14, 2021
f5f0d72
Ignore ffmpeg-dash patches
davedoesdev Oct 14, 2021
5924973
Fixes for DASH
davedoesdev Oct 16, 2021
dab1112
Allow HTTP method to be specified for HLS and DASH
davedoesdev Oct 16, 2021
2aee0a5
Allow DASH and HLS upload options to be specified
davedoesdev Oct 16, 2021
95aeb49
Update README for DASH
davedoesdev Oct 17, 2021
8b9ee57
Update README to reflect default request method
davedoesdev Oct 17, 2021
602db8f
HLS/DASH: If fetch mode isn't no-cors, log error
davedoesdev Oct 19, 2021
d38348c
postMessage if upload URL starts with postMessage:
davedoesdev Dec 9, 2021
c99d521
postMessage: transfer readable stream
davedoesdev Dec 9, 2021
8abbe51
Update README for HLS/DASH postMessage
davedoesdev Dec 10, 2021
233c56e
Update deps, fix compile with emscripten 3.1.13
davedoesdev Jun 10, 2022
42191f5
Don't exit if fetch requests are pending
davedoesdev Jul 24, 2022
7257e61
Update ffmpeg to n5.1
davedoesdev Jul 24, 2022
d1b1559
Update ffmpeg to n6.0
davedoesdev May 13, 2023
59767be
Update patches to apply cleanly
davedoesdev May 17, 2023
1b61fc6
ffmpeg needs pthreads now
davedoesdev May 17, 2023
7f3fe0e
Fixes for threading
davedoesdev Nov 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -22,3 +22,11 @@
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
ignore = dirty
[submodule "build/ffmpeg-dash"]
path = build/ffmpeg-dash
url = https://git.ffmpeg.org/ffmpeg.git
ignore = dirty
9 changes: 4 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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
&& cd /root/emsdk && ./emsdk install latest && ./emsdk activate latest
149 changes: 128 additions & 21 deletions Makefile
Original file line number Diff line number Diff line change
@@ -6,6 +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 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
@@ -26,15 +27,42 @@ 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 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 = libopus 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

LIBRARY_DASH_JS = build/library-dash.js
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
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: 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-ffmpeg-dash
clean-js:
rm -f ffmpeg*.js
clean-wasm:
rm -f ffmpeg*.wasm
clean-opus:
cd build/opus && git clean -xdf
clean-libvpx:
@@ -47,6 +75,10 @@ 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
clean-ffmpeg-dash:
cd build/ffmpeg-dash && git clean -xdf

build/opus/configure:
cd build/opus && ./autogen.sh
@@ -144,7 +176,7 @@ build/x264/dist/lib/libx264.so:
# - <https://kripken.github.io/emscripten-site/docs/compiling/Building-Projects.html>
# - <https://github.com/kripken/emscripten/issues/831>
# - <https://ffmpeg.org/pipermail/libav-user/2013-February/003698.html>
FFMPEG_COMMON_ARGS = \
FFMPEG_COMMON_CORE_ARGS = \
--cc=emcc \
--ranlib=emranlib \
--enable-cross-compile \
@@ -153,14 +185,14 @@ FFMPEG_COMMON_ARGS = \
--disable-runtime-cpudetect \
--disable-asm \
--disable-fast-unaligned \
--disable-pthreads \
--disable-w32threads \
--disable-os2threads \
--disable-debug \
--disable-stripping \
--disable-safe-bitstream-reader \
\
--disable-all \
--enable-pthreads \
--enable-ffmpeg \
--enable-avcodec \
--enable-avformat \
@@ -172,17 +204,20 @@ FFMPEG_COMMON_ARGS = \
--disable-dxva2 \
--disable-vaapi \
--disable-vdpau \
$(addprefix --enable-decoder=,$(COMMON_DECODERS)) \
$(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \
--enable-protocol=file \
$(addprefix --enable-filter=,$(COMMON_FILTERS)) \
--disable-bzlib \
--disable-iconv \
--disable-libxcb \
--disable-lzma \
--disable-sdl2 \
--disable-securetransport \
--disable-xlib \
--disable-xlib

FFMPEG_COMMON_ARGS = \
$(FFMPEG_COMMON_CORE_ARGS) \
$(addprefix --enable-decoder=,$(COMMON_DECODERS)) \
$(addprefix --enable-demuxer=,$(COMMON_DEMUXERS)) \
$(addprefix --enable-filter=,$(COMMON_FILTERS)) \
--enable-zlib

build/ffmpeg-webm/ffmpeg.bc: $(WEBM_SHARED_DEPS)
@@ -194,10 +229,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,25 +243,76 @@ 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 = \
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 && \
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)) \
$(addprefix --enable-muxer=,$(HLS_MUXERS)) \
$(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 \
--extra-ldflags="-r" \
&& \
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-async-exit.patch && \
patch -p1 < ../ffmpeg-pthread-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" \
&& \
emmake make -j EXESUF=.bc

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 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 $@

EMCC_COMMON_ARGS = \
$(EMCC_COMMON_CORE_ARGS) \
-s NODEJS_CATCH_EXIT=0 \
-s NODEJS_CATCH_REJECTION=0 \
-s TOTAL_MEMORY=67108864 \
-lnodefs.js -lworkerfs.js \
--pre-js $(PRE_JS) \
-o $@
-s WASM=0

ffmpeg-webm.js: $(FFMPEG_WEBM_BC) $(PRE_JS) $(POST_JS_SYNC)
emcc $(FFMPEG_WEBM_BC) $(WEBM_SHARED_DEPS) \
@@ -248,3 +333,25 @@ 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) -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_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"]'

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) \
-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"]'
70 changes: 65 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ 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.
* `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.

@@ -61,9 +63,67 @@ ffmpeg.js also provides wrapper for main function with Web Worker interface to o
* `{type: "done", data: "<result>"}` - Job finished with some result.
* `{type: "error", data: "<error description>"}` - Error occurred.
* `{type: "abort", data: "<abort reason>"}` - FFmpeg terminated abnormally (e.g. out of memory, wasm error).
* 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: "<code>"}` - FFmpeg exited with status code.
* `{type: "upload", stream: "<chunk as ReadableStream>", url: "<chunk filename>"}` - 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.
* For HLS, 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', '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', // if you don't have < 2s keyframes
'/outbound/output.m3u8' // path to media playlist file in virtual FS,
// must be under /outbound
],
MEMFS: [
{ name: 'stream1' },
{ name: 'stream2' }
]
```
* 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: "<upload-url>", protocol: "hls|dash", options: "<fetch request 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: "<filename>", data: "<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");
@@ -141,7 +201,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.
@@ -152,7 +212,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
@@ -173,9 +233,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).
@@ -185,7 +245,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).
20 changes: 20 additions & 0 deletions build/ffmpeg-async-exit.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c
index a1de621d1c..88bbf4cc79 100644
--- a/fftools/cmdutils.c
+++ b/fftools/cmdutils.c
@@ -96,12 +96,14 @@ void report_and_exit(int ret)
exit_program(AVUNERROR(ret));
}

+int emscripten_exit_async(int code);
+
void exit_program(int ret)
{
if (program_exit)
program_exit(ret);

- exit(ret);
+ exit(emscripten_exit_async(ret));
}

double parse_number_or_die(const char *context, const char *numstr, int type,
26 changes: 26 additions & 0 deletions build/ffmpeg-async-io.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
diff --git a/libavformat/file.c b/libavformat/file.c
index cbdf48de0a..1e53e7f647 100644
--- a/libavformat/file.c
+++ b/libavformat/file.c
@@ -41,6 +41,8 @@
#include "os_support.h"
#include "url.h"

+extern int emscripten_read_async(int fd, unsigned char* buf, int size);
+
/* Some systems may not have S_ISFIFO */
#ifndef S_ISFIFO
# ifdef S_IFIFO
@@ -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);
- ret = read(c->fd, buf, size);
+ if ((c->fd == 3) || (c->fd == 4)) {
+ ret = emscripten_read_async(c->fd, buf, size);
+ } else {
+ ret = read(c->fd, buf, size);
+ }
if (ret == 0 && c->follow)
return AVERROR(EAGAIN);
if (ret == 0)
1 change: 1 addition & 0 deletions build/ffmpeg-dash
Submodule ffmpeg-dash added at ea3d24
101 changes: 101 additions & 0 deletions build/ffmpeg-dash-codecs.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 17fe5f430c..dbbd504ef4 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -368,83 +368,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 47bbbbfb2a..cafa1879bd 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -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
-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
12 changes: 12 additions & 0 deletions build/ffmpeg-dash-configure.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/configure b/configure
index b6616f00b6..d102f263f6 100755
--- a/configure
+++ b/configure
@@ -3421,7 +3421,6 @@ avi_muxer_select="riffenc"
avif_muxer_select="mov_muxer"
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"
1 change: 1 addition & 0 deletions build/ffmpeg-hls
Submodule ffmpeg-hls added at ea3d24
22 changes: 22 additions & 0 deletions build/ffmpeg-hls-configure.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
diff --git a/configure b/configure
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"
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="pcm_rechunk_bsf"
mxf_d10_muxer_select="mxf_muxer"
2 changes: 1 addition & 1 deletion build/ffmpeg-mp4
Submodule ffmpeg-mp4 updated from 192d1d to ea3d24
13 changes: 13 additions & 0 deletions build/ffmpeg-pthread-exit.patch
Original file line number Diff line number Diff line change
@@ -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;
}

2 changes: 1 addition & 1 deletion build/ffmpeg-webm
Submodule ffmpeg-webm updated from 192d1d to ea3d24
1 change: 1 addition & 0 deletions build/library-dash.js
110 changes: 110 additions & 0 deletions build/library-hls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
mergeInto(LibraryManager.library, {
emscripten_read_async: function (fd, buf, size) {
if (!self.stream_started) {
const onmessage = self.onmessage;
self.stream_is_main = true;
self.stream_ended = false;
self.stream_sending = 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.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.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.stream_ended) {
const handler = self.stream_handlers.get(name);
self.stream_handlers.delete(name);
self.stream_bufs.delete(name);
self.stream_sizes.delete(name);
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'], []);
}
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 '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-stream'});
self.stream_started = true;
}
return Asyncify.handleSleep(wakeUp => {
if (size <= 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});
}
}
self.stream_handlers.set(name, wakeUp);
self.stream_bufs.set(name, buf);
self.stream_sizes.set(name, size);
self.stream_process(name);
});
},
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': code
});
}
});
}
});
2 changes: 1 addition & 1 deletion build/libvpx
Submodule libvpx updated from 7ec7a3 to d6eb96
2 changes: 1 addition & 1 deletion build/libvpx-fix-ld.patch
Original file line number Diff line number Diff line change
@@ -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) \
2 changes: 1 addition & 1 deletion build/opus
Submodule opus updated 130 files
368 changes: 326 additions & 42 deletions build/post-worker.js
Original file line number Diff line number Diff line change
@@ -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"});
}
230 changes: 115 additions & 115 deletions build/pre.js
Original file line number Diff line number Diff line change
@@ -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
// <https://github.com/kripken/emscripten/issues/3663> 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
// <https://github.com/kripken/emscripten/issues/3663> 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});
};
}
2 changes: 1 addition & 1 deletion build/x264
Submodule x264 updated from 296494 to baee40
1,903 changes: 1,318 additions & 585 deletions package-lock.json

Large diffs are not rendered by default.