Skip to content

HLS (HTTP Live Streaming) and DASH support #166

New issue

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

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

Already on GitHub? # to your account

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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 \
Expand All @@ -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 \
Expand All @@ -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)
Expand All @@ -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 && \
Expand All @@ -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) \
Expand All @@ -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
Expand Up @@ -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.

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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).
Expand All @@ -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).
Expand Down
Loading