diff --git a/.gitignore b/.gitignore
index 18f45fa4e04d1926aafd52048a865484b7baa505..92c9b7ea52f541b70e6f6a989c7fdbed83ab681a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 # Ignore build dirs
+samples/
 vlc/
 emsdk/
 # and files
@@ -6,5 +7,5 @@ experimental.data
 experimental.html
 experimental.js
 experimental.wasm
+experimental.wasm.map
 experimental.worker.js
-
diff --git a/compile.sh b/compile.sh
index 04bac1b1d6ab9330e93b47ffe0951bf0ba1730e3..ec18c0409e205e2640bf598c46add205817d0362 100755
--- a/compile.sh
+++ b/compile.sh
@@ -1,4 +1,4 @@
-#! /bin/sh
+#!/bin/sh
 set -e
 
 ## FUNCTIONS
@@ -15,175 +15,57 @@ checkfail()
         exit 1
     fi
 }
+
+SLOW_MODE=${SLOW_MODE:=1}
 WORK_DIR=$PWD
 
+EMSDK_VERSION="2.0.25"
 # Download the portable SDK and uncompress it
 if [ ! -d emsdk ]; then
     diagnostic "emsdk not found. Fetching it"
     git clone http://github.com/emscripten-core/emsdk.git emsdk
-    cd emsdk && ./emsdk update-tags && ./emsdk install tot-upstream && ./emsdk activate tot-upstream
+    cd emsdk && ./emsdk update-tags && ./emsdk install ${EMSDK_VERSION} && ./emsdk activate ${EMSDK_VERSION}
     checkfail "emsdk: fetch failed"
 fi
 
 cd $WORK_DIR
-TESTED_HASH="7bad2a86"
+TESTED_HASH="3379c7bdba42984d56d311fcdc9810308b3a08b7"
 # Go go go vlc
 if [ ! -d vlc ]; then
     diagnostic "VLC source not found, cloning"
-    git clone http://git.videolan.org/git/vlc.git vlc || checkfail "VLC source: git clone failed"
+    git clone https://code.videolan.org/videolan/vlc.git vlc || checkfail "VLC source: git clone failed"
     cd vlc
     diagnostic "VLC source: resetting to the TESTED_HASH commit (${TESTED_HASH})"
     git reset --hard ${TESTED_HASH} || checkfail "VLC source: TESTED_HASH ${TESTED_HASH} not found"
-    cd ..
-    checkfail "vlc source: git clone failed"
-fi
-
-cd vlc
-
-# Make in //
-if [ -z "$MAKEFLAGS" ]; then
-    UNAMES=$(uname -s)
-    MAKEFLAGS=
-    if which nproc >/dev/null; then
-        MAKEFLAGS=-j`nproc`
-    elif [ "$UNAMES" == "Darwin" ] && which sysctl >/dev/null; then
-        MAKEFLAGS=-j`sysctl -n machdep.cpu.thread_count`
+    # patching vlc
+    if [ -d ../vlc_patches ] && [ "$(ls -A ../vlc_patches)" ]; then
+	# core patches
+	git am -3 ../vlc_patches/0001-configure-improve-testing-unsupported-GL-functions-f.patch
+	git am -3 ../vlc_patches/0001-modules-disable-libvlc_json-and-ytbdl-vlc.js-17.patch
+	git am -3 ../vlc_patches/nacl-wasm/00*.patch
+	git am -3 ../vlc_patches/audio_output/00*.patch
+	git am -3 ../vlc_patches/video_output/00*.patch
+	git am -3 ../vlc_patches/logger/00*.patch
+	# git am -3 ../vlc_patches/filesystem/*.patch
     fi
+    checkfail "vlc source: git clone failed"
 fi
 
-# VLC tools
-export PATH=`pwd`/extras/tools/build/bin:$PATH
-echo "Building tools"
-cd extras/tools
-./bootstrap
-checkfail "buildsystem tools: bootstrap failed"
-make $MAKEFLAGS
-checkfail "buildsystem tools: make"
-
 cd $WORK_DIR
-
 diagnostic "Setting the environment"
-source emsdk/emsdk_env.sh
-export PKG_CONFIG_PATH=$EMSDK/emscripten/incoming/system/lib/pkgconfig
-export PKG_CONFIG_LIBDIR=$PWD/vlc/contrib/wasm32_unknowm_emscripten/lib/pkgconfig
-export PKG_CONFIG_PATH_CUSTOM=$PKG_CONFIG_LIBDIR
+. emsdk/emsdk_env.sh
 
-# Check that clang is working
-clang --version
-
-diagnostic "Patching"
-
-cd vlc
-
-# patching vlc
-if [ -d ../vlc_patches ] && [ "$(ls -A ../vlc_patches)" ]; then
-    # core patches
-    git am -3 ../vlc_patches/0001-contrib-add-emscripten-target.patch
-    git am -3 ../vlc_patches/0002-contrib-add-ffmpeg-configuration-options-for-wasm-em.patch
-    git am -3 ../vlc_patches/0003-contrib-delete-empty-variable.patch
-    git am -3 ../vlc_patches/0006-configure-Create-a-target-for-emscripten-in-the-conf.patch
-    git am -3 ../vlc_patches/0007-core-initial-core-build-for-emscripten-based-on-POSI.patch
-    git am -3 ../vlc_patches/0008-compat-add-sigwait-support-for-emscripten.patch
-    git am -3 ../vlc_patches/0009-compat-add-clock_nanosleep-support.patch
-    git am -3 ../vlc_patches/0010-emscripten-add-vlc_getProxyUrl-stub.patch
-    git am -3 ../vlc_patches/0011-configure-disable-deprecated-GL-functions-for-emscri.patch
-    git am -3 ../vlc_patches/0012-logger-add-emscripten-module.patch
-    git am -3 ../vlc_patches/0013-window-add-emscripten-type.patch
-    git am -3 ../vlc_patches/0014-vout-add-emscripten-gl-es2-and-window-modules.patch
-    git am -3 ../vlc_patches/0015-vlc_common-add-weak-attribute-support-for-wasm.patch
-    git am -3 ../vlc_patches/0016-Add-meson_system_name-for-emscripten.patch
-    
-    # Add OPENAL support
-    git am -3 ../vlc_patches/openal/*
-fi
-
-# BOOTSTRAP
-
-if [ ! -f configure ]; then
-    echo "Bootstraping"
-    ./bootstrap
-    checkfail "vlc: bootstrap failed"
-fi
-
-############
-# Contribs #
-############
-
-echo "Building the contribs"
-mkdir -p contrib/contrib-emscripten
-cd contrib/contrib-emscripten
-
-    ../bootstrap --disable-disc --disable-gpl --disable-sout \
-    --disable-network \
-    --host=wasm32-unknown-emscripten --build=x86_64-linux
-checkfail "contribs: bootstrap failed"
-
-emmake make list
-emmake make $MAKEFLAGS fetch
-checkfail "contribs: make fetch failed"
-emmake make $MAKEFLAGS .ffmpeg
-
-checkfail "contribs: make failed"
-
-cd ../../
-
-# Build
-mkdir -p build-emscripten && cd build-emscripten
-
-OPTIONS="
-    --host=wasm32-unknown-emscripten
-    --enable-debug
-    --enable-gles2
-    --disable-lua
-    --disable-ssp
-    --disable-nls
-    --disable-sout
-    --disable-vlm
-    --disable-addonmanagermodules
-    --enable-avcodec
-    --enable-merge-ffmpeg
-    --disable-swscale
-    --disable-a52
-    --disable-x264
-    --disable-xcb
-    --disable-alsa
-    --disable-macosx
-    --disable-sparkle
-    --disable-qt
-    --disable-screen
-    --disable-xcb
-    --disable-pulse
-    --disable-alsa
-    --disable-oss
-    --disable-vlc"
-#     --disable-xvideo Unknown option
-# Note :
-#        search.h is a blacklisted module
-#        time.h is a blacklisted module
-#        shm.h is a blacklisted module
-#        ssp is not supported on the wasm backend
-
-emconfigure ../configure ${OPTIONS}  \
-	    ac_cv_func_sendmsg=yes ac_cv_func_recvmsg=yes ac_cv_func_if_nameindex=yes ac_cv_header_search_h=no ac_cv_header_time_h=no ac_cv_header_sys_shm_h=no
-
-emmake make ${MAKEFLAGS}
-
-diagnostic "Generating module list"
-cd ../..
-./generate_modules_list.sh
-cd vlc/build-emscripten
-emcc vlc-modules.c -o vlc-modules.bc -pthread
-cd ../..
+diagnostic "build libvlc"
+cd ./vlc/extras/package/wasm-emscripten/
+./build.sh --mode=${SLOW_MODE}
 
 url="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
 
-# copy Dolby_Canyon.vob
 diagnostic "getting video"
-cd vlc/build-emscripten/
-curl ${url} -o BigBuckBunny.mp4
-
 cd $WORK_DIR
-
+mkdir -p samples/
+if [ ! -f "./samples/BigBuckBunny.mp4" ]; then
+    curl ${url} -o samples/BigBuckBunny.mp4
+fi
 diagnostic "Generating executable"
-cp main.c vlc/build-emscripten/
 ./create_main.sh
diff --git a/create_main.sh b/create_main.sh
index ed333faebd4a7455cd43128d0523f067a6ba9ef1..32b8cb41b6f04f5297bf6cfb25306a64f8968dc3 100755
--- a/create_main.sh
+++ b/create_main.sh
@@ -23,16 +23,23 @@ if [ ! -d vlc ]; then
     exit 1
 fi
 
-PROJECT_DIR=$(pwd)/vlc
+PATH_VLC=${PATH_VLC:=./vlc}
+SAMPLE_DIR=${SAMPLE_DIR:=./samples}
 
-cd vlc/build-emscripten
-# for release, remove profiling-funcs and add -Os
-emcc -s USE_PTHREADS=1 -s TOTAL_MEMORY=1GB  \
-    -s OFFSCREEN_FRAMEBUFFER=1 --profiling-funcs \
-    -I $PROJECT_DIR/include/ -I $PROJECT_DIR/contrib/wasm32-unknown-emscripten/include/ main.c \
-    $PROJECT_DIR/build-emscripten/lib/.libs/libvlc.a \
-    vlc-modules.bc $PROJECT_DIR/build-emscripten/modules/.libs/*.a \
-    $PROJECT_DIR/contrib/wasm32-unknown-emscripten/lib/*.a \
-    $PROJECT_DIR/build-emscripten/src/.libs/libvlccore.a \
-    $PROJECT_DIR/build-emscripten/compat/.libs/libcompat.a \
-    -o ../../experimental.html --emrun --preload-file BigBuckBunny.mp4
+# For release builds, remove '--profiling-funcs' and add '-Os'
+# Note that we use '-s MODULARIZE', but no '-s EXPORT_ES6', which would
+# conflict with pthreads on Firefox.
+emcc --bind -s USE_PTHREADS=1 -s TOTAL_MEMORY=1GB -s PTHREAD_POOL_SIZE=15 \
+    -s OFFSCREEN_FRAMEBUFFER=1 -s USE_WEBGL2=1 --profiling-funcs \
+    -s MODULARIZE=1 -s EXPORT_NAME="VlcModule" \
+    -s EXTRA_EXPORTED_RUNTIME_METHODS="allocateUTF8" \
+    -I $PATH_VLC/include/ -I $PATH_VLC/wasm32-unknown-emscripten/include/ \
+    main.c exports_media_player.c exports_media.c \
+    $PATH_VLC/build-emscripten/lib/.libs/libvlc.a \
+    $PATH_VLC/build-emscripten/vlc-modules.bc \
+    $PATH_VLC/build-emscripten/modules/.libs/*.a \
+    $PATH_VLC/contrib/wasm32-unknown-emscripten/lib/*.a \
+    $PATH_VLC/build-emscripten/src/.libs/libvlccore.a \
+    $PATH_VLC/build-emscripten/compat/.libs/libcompat.a \
+     --js-library lib/wasm-imports.js \
+    -o experimental.js --preload-file ${SAMPLE_DIR}
diff --git a/exports_media.c b/exports_media.c
new file mode 100644
index 0000000000000000000000000000000000000000..55d35e13faaf4050bd3b82f9b187c9336b6bf010
--- /dev/null
+++ b/exports_media.c
@@ -0,0 +1,23 @@
+// Re-exports of functions defined in "include/vlc/libvlc/media/player.h"
+// See exports_media_player.c for why this is necessary.
+
+#include <vlc/vlc.h>
+#include <vlc_common.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+// Singleton, defined in main.c
+extern libvlc_instance_t *libvlc;
+
+libvlc_media_t* EMSCRIPTEN_KEEPALIVE wasm_media_new_path(const char *path) {
+  return libvlc_media_new_path(libvlc, path);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_retain( libvlc_media_t *media) {
+  libvlc_media_retain(media);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_release( libvlc_media_t *media) {
+  libvlc_media_release(media);
+}
diff --git a/exports_media_player.c b/exports_media_player.c
new file mode 100644
index 0000000000000000000000000000000000000000..af61c3777543f27ea51f4259ba763529e5ac4396
--- /dev/null
+++ b/exports_media_player.c
@@ -0,0 +1,236 @@
+// Re-exports of functions defined in "include/vlc/libvlc/media/player.h"
+// We need to re-export these functions to make sure they're included as
+// symbols in the wasm binary.
+// Emscripten provides two ways to do that: EMSCRIPTEN_KEEPALIVE, and the
+// EXPORTED_FUNCTIONS argument. EXPORTED_FUNCTIONS is not reliable, because
+// symbols might be inlined in intermediary passes.
+// Also, some functions need some marshalling of arguments to be callabled
+// from JS.
+
+#include <vlc/vlc.h>
+#include <vlc_common.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+// Singleton, defined in main.c
+extern libvlc_instance_t *libvlc;
+
+libvlc_media_player_t* EMSCRIPTEN_KEEPALIVE wasm_media_player_new() {
+  return libvlc_media_player_new(libvlc);
+}
+
+libvlc_media_player_t* EMSCRIPTEN_KEEPALIVE wasm_media_player_new_from_media(libvlc_media_t* media) {
+  return libvlc_media_player_new_from_media(media);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_release(libvlc_media_player_t *media_player) {
+  libvlc_media_player_release(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasmc_media_player_retain(libvlc_media_player_t *media_player) {
+  libvlc_media_player_retain(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_set_media(libvlc_media_player_t *media_player, libvlc_media_t *media) {
+  libvlc_media_player_set_media(media_player, media);
+}
+
+libvlc_media_t* EMSCRIPTEN_KEEPALIVE wasm_media_player_get_media(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_media(media_player);
+}
+
+// TODO
+// LIBVLC_API libvlc_event_manager_t * libvlc_media_player_event_manager (libvlc_media_player_t *p_mi );
+
+EM_BOOL EMSCRIPTEN_KEEPALIVE wasm_media_player_is_playing(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_is_playing(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_play(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_play(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_set_pause(libvlc_media_player_t *media_player, int do_pause) {
+  libvlc_media_player_set_pause(media_player, do_pause);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_pause(libvlc_media_player_t *media_player) {
+  libvlc_media_player_pause(media_player);
+}
+
+// TODO
+// LIBVLC_API int libvlc_media_player_stop_async ( libvlc_media_player_t *p_mi );
+
+
+libvlc_time_t EMSCRIPTEN_KEEPALIVE wasm_media_player_get_length(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_length(media_player);
+}
+
+libvlc_time_t EMSCRIPTEN_KEEPALIVE wasm_media_player_get_time(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_time(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_set_time(libvlc_media_player_t *media_player, libvlc_time_t time, bool fast) {
+  return libvlc_media_player_set_time(media_player, time, fast);
+}
+
+float EMSCRIPTEN_KEEPALIVE wasm_media_player_get_position(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_position(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_set_position(libvlc_media_player_t *media_player, float position, EM_BOOL fast) {
+  return libvlc_media_player_set_position(media_player, position, fast);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_set_chapter(libvlc_media_player_t *media_player, int chapter) {
+  libvlc_media_player_set_chapter(media_player, chapter);
+}
+
+float EMSCRIPTEN_KEEPALIVE wasm_media_player_get_chapter(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_chapter(media_player);
+}
+
+float EMSCRIPTEN_KEEPALIVE wasm_media_player_get_chapter_count(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_chapter_count(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_get_chapter_count_for_title(libvlc_media_player_t *media_player, int title) {
+  return libvlc_media_player_get_chapter_count_for_title(media_player, title);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_set_title(libvlc_media_player_t *media_player, int title) {
+  libvlc_media_player_set_title(media_player, title);
+}
+
+float EMSCRIPTEN_KEEPALIVE wasm_media_player_get_title(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_title(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_get_title_count( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_get_title_count(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_previous_chapter( libvlc_media_player_t *media_player ) {
+  libvlc_media_player_previous_chapter(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_next_chapter( libvlc_media_player_t *media_player ) {
+  libvlc_media_player_next_chapter(media_player);
+}
+
+float EMSCRIPTEN_KEEPALIVE wasm_media_player_get_rate( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_get_rate(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_set_rate( libvlc_media_player_t *media_player, float rate ) {
+  return libvlc_media_player_set_rate(media_player, rate);
+}
+
+
+unsigned EMSCRIPTEN_KEEPALIVE wasm_media_player_has_vout( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_has_vout(media_player);
+}
+
+EM_BOOL EMSCRIPTEN_KEEPALIVE wasm_media_player_is_seekable( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_is_seekable(media_player);
+}
+
+EM_BOOL EMSCRIPTEN_KEEPALIVE wasm_media_player_can_pause( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_can_pause(media_player);
+}
+
+EM_BOOL EMSCRIPTEN_KEEPALIVE wasm_media_player_program_scrambled( libvlc_media_player_t *media_player ) {
+  return libvlc_media_player_program_scrambled(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_media_player_next_frame( libvlc_media_player_t *media_player ) {
+  libvlc_media_player_next_frame(media_player);
+}
+
+
+int EMSCRIPTEN_KEEPALIVE wasm_video_get_size_x(libvlc_media_player_t *media_player, unsigned num) {
+  unsigned x = 0;
+  unsigned y = 0;
+  int res = libvlc_video_get_size(media_player, num, &x, &y);
+  if (res == -1)
+    return -1;
+  else
+    return x;
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_video_get_size_y(libvlc_media_player_t *media_player, unsigned num) {
+  unsigned x = 0;
+  unsigned y = 0;
+  int res = libvlc_video_get_size(media_player, num, &x, &y);
+  if (res == -1)
+    return -1;
+  else
+    return y;
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_video_get_cursor_x(libvlc_media_player_t *media_player, unsigned num) {
+  int x = 0;
+  int y = 0;
+  int res = libvlc_video_get_cursor(media_player, num, &x, &y);
+  if (res == -1)
+    return -1;
+  else
+    return x;
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_video_get_cursor_y(libvlc_media_player_t *media_player, unsigned num) {
+  int x = 0;
+  int y = 0;
+  int res = libvlc_video_get_cursor(media_player, num, &x, &y);
+  if (res == -1)
+    return -1;
+  else
+    return y;
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_audio_toggle_mute(libvlc_media_player_t *media_player) {
+  libvlc_audio_toggle_mute(media_player);
+}
+
+EM_BOOL EMSCRIPTEN_KEEPALIVE wasm_audio_get_mute(libvlc_media_player_t *media_player) {
+  return libvlc_audio_get_mute(media_player);
+}
+
+void EMSCRIPTEN_KEEPALIVE wasm_audio_set_mute(libvlc_media_player_t *media_player, int status) {
+  libvlc_audio_set_mute(media_player, status);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_get_volume(libvlc_media_player_t *media_player) {
+  return libvlc_audio_get_volume(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_set_volume(libvlc_media_player_t *media_player, int volume) {
+  return libvlc_audio_set_volume(media_player, volume);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_get_channel(libvlc_media_player_t *media_player) {
+  return libvlc_audio_get_channel(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_set_channel(libvlc_media_player_t *media_player, int channel) {
+  return libvlc_audio_set_channel(media_player, channel);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_get_delay(libvlc_media_player_t *media_player) {
+  return libvlc_audio_get_delay(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_audio_set_delay(libvlc_media_player_t *media_player, int delay) {
+  return libvlc_audio_set_delay(media_player, delay);
+}
+
+// TODO - Export libvlc_media_player_role constants
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_get_role(libvlc_media_player_t *media_player) {
+  return libvlc_media_player_get_role(media_player);
+}
+
+int EMSCRIPTEN_KEEPALIVE wasm_media_player_set_role(libvlc_media_player_t *media_player, unsigned role) {
+  return libvlc_media_player_set_role(media_player, role);
+}
diff --git a/generate_modules_list.sh b/generate_modules_list.sh
deleted file mode 100755
index 9c3e3ebf775c9005d69e79c037d417dfc0b0fbf9..0000000000000000000000000000000000000000
--- a/generate_modules_list.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-set -e
-
-## FUNCTIONS
-
-diagnostic()
-{
-     echo "$@" 1>&2;
-}
-
-checkfail()
-{
-    if [ ! $? -eq 0 ];then
-        diagnostic "$1"
-        exit 1
-    fi
-}
-
-get_symbol()
-{
-    echo "$1" | grep vlc_entry_$2 | cut -d " " -f 3
-}
-
-source emsdk/emsdk_env.sh
-
-if [ ! -d vlc ]; then
-    diagnostic "vlc must exists. Execute compile.sh"
-	exit 1
-fi
-
-cd vlc
-
-PROJECT_DIR=$(pwd)
-NM="$EMSDK/upstream/bin/llvm-nm"
-
-cd build-emscripten/modules/.libs
-
-# create module list
-echo "creating module list"
-touch $PROJECT_DIR/build-emscripten/vlc-modules.c
-echo -e "// This file is autogenerated" > $PROJECT_DIR/build-emscripten/vlc-modules.c
-echo -e "#include <unistd.h>\n\n" >> $PROJECT_DIR/build-emscripten/vlc-modules.c
-
-
-BUILTINS="const void *vlc_static_modules[] = {\n"
-LDFLAGS=""
-DEFINITIONS=""
-VLCMODULES=""
-i=""
-
-for i in `ls *plugin.a`
-do
-    VLCMODULES="$i $VLCMODULES"
-done
-
-for file in $VLCMODULES
-    do
-        symbols=$($NM -g $file)
-        entryname=$(get_symbol "$symbols" _)
-        DEFINITIONS+="int $entryname (int (*)(void *, void *, int, ...), void *);\n";
-        BUILTINS+=" $entryname,\n"
-        LDFLAGS+="\$PROJECT_DIR/build-emscripten/modules/.libs/$file "
-    done;
-
-cd ../..
-
-BUILTINS="$BUILTINS NULL\n};\n"
-
-echo -e "$DEFINITIONS\n$BUILTINS" >> $PROJECT_DIR/build-emscripten/vlc-modules.c
diff --git a/lib/libvlc.js b/lib/libvlc.js
new file mode 100644
index 0000000000000000000000000000000000000000..51a8e4d2bbd5a2d4813d400b3c765896a8505717
--- /dev/null
+++ b/lib/libvlc.js
@@ -0,0 +1,244 @@
+// Encapsulate functions exported from exports_*.c
+
+// Encapsulates libvlc_media_player_t
+export class MediaPlayer {
+  constructor(module, path) {
+    this.module = module;
+    this.media_player_ptr = module._wasm_media_player_new();
+    module._attach_update_events(this.media_player_ptr);
+
+    if (path != null) {
+      let media = new Media(module, path);
+      this.set_media(media);
+      media.release();
+    }
+  }
+
+  release() {
+    this.module._wasm_media_player_release(this.media_player_ptr);
+    this.media_player_ptr = 0;
+  }
+
+  set_media(media) {
+    // TODO - assert typeof
+    this.module._wasm_media_player_set_media(this.media_player_ptr, media.media_ptr);
+  }
+
+  get_media() {
+    let media_ptr = this.module._wasm_media_player_get_media(this.media_player_ptr);
+    this.module._wasm_media_retain(media_ptr);
+    // Build from raw ptr
+    return new Media(this.module, null, media_ptr);
+  }
+
+  toggle_play() {
+    if (!this.is_playing()) {
+      this.play();
+    }
+    else {
+      this.pause();
+    }
+  }
+
+  is_playing() {
+    return this.module._wasm_media_player_is_playing(this.media_player_ptr);
+  }
+
+  play() {
+    return this.module._wasm_media_player_play(this.media_player_ptr);
+  }
+
+  set_pause(do_pause) {
+    return this.module._wasm_media_player_set_pause(this.media_player_ptr, do_pause);
+  }
+
+  pause() {
+    return this.module._wasm_media_player_pause(this.media_player_ptr);
+  }
+
+  get_length() {
+    return this.module._wasm_media_player_get_length(this.media_player_ptr);
+  }
+
+  get_time() {
+    return this.module._wasm_media_player_get_time(this.media_player_ptr);
+  }
+
+  set_time(time, fast = false) {
+    // TODO - what does "fast" argument do?
+    return this.module._wasm_media_player_set_time(this.media_player_ptr, time, fast);
+  }
+
+  get_position() {
+    return this.module._wasm_media_player_get_position(this.media_player_ptr);
+  }
+
+  set_position(position, fast = false) {
+    // TODO - what does "fast" argument do?
+    return this.module._wasm_media_player_set_position(this.media_player_ptr, position, fast);
+  }
+
+  set_chapter(chapter) {
+    this.module._wasm_media_player_set_chapter(this.media_player_ptr, chapter);
+  }
+
+  get_chapter() {
+    return this.module._wasm_media_player_get_chapter(this.media_player_ptr);
+  }
+
+  get_chapter_count() {
+    return this.module._wasm_media_player_get_chapter_count(this.media_player_ptr);
+  }
+
+  get_chapter_count_for_title(title) {
+    return this.module._wasm_media_player_get_chapter_count_for_title(this.media_player_ptr, title);
+  }
+
+  set_title(title) {
+    this.module._wasm_media_player_set_title(this.media_player_ptr, title);
+  }
+
+  get_title() {
+    return this.module._wasm_media_player_get_title(this.media_player_ptr);
+  }
+
+  get_title_count() {
+    return this.module._wasm_media_player_get_title_count(this.media_player_ptr);
+  }
+
+  previous_chapter() {
+    return this.module._wasm_media_player_previous_chapter(this.media_player_ptr);
+  }
+
+  next_chapter() {
+    return this.module._wasm_media_player_next_chapter(this.media_player_ptr);
+  }
+
+  get_rate() {
+    return this.module._wasm_media_player_get_rate(this.media_player_ptr);
+  }
+
+  set_rate(rate) {
+    return this.module._wasm_media_player_set_rate(this.media_player_ptr, rate);
+  }
+
+  has_vout() {
+    return this.module._wasm_media_player_has_vout(this.media_player_ptr);
+  }
+
+  is_seekable() {
+    return this.module._wasm_media_player_is_seekable(this.media_player_ptr);
+  }
+
+  can_pause() {
+    return this.module._wasm_media_player_can_pause(this.media_player_ptr);
+  }
+
+  program_scrambled() {
+    return this.module._wasm_media_player_program_scrambled(this.media_player_ptr);
+  }
+
+  next_frame() {
+    return this.module._wasm_media_player_next_frame(this.media_player_ptr);
+  }
+
+  get_size() {
+    let x = this.module._wasm_video_get_size_x(this.media_player_ptr);
+    let y = this.module._wasm_video_get_size_y(this.media_player_ptr);
+
+    if (x == -1 || y == -1) {
+      // TODO - give more context
+      throw new Error("Cannot get video size");
+    }
+
+    return { x, y };
+  }
+
+  get_cursor() {
+    let x = this.module._wasm_video_get_cursor_x(this.media_player_ptr);
+    let y = this.module._wasm_video_get_cursor_y(this.media_player_ptr);
+
+    if (x == -1 || y == -1) {
+      // TODO - give more context
+      throw new Error("Cannot get video cursor");
+    }
+
+    return { x, y };
+  }
+
+  toggle_mute() {
+    this.module._wasm_audio_toggle_mute(this.media_player_ptr);
+  }
+
+  get_mute() {
+    return this.module._wasm_audio_get_mute(this.media_player_ptr);
+  }
+
+  set_mute(mute) {
+    this.module._wasm_audio_set_mute(this.media_player_ptr, mute);
+  }
+
+  get_volume() {
+    return this.module._wasm_audio_get_volume(this.media_player_ptr);
+  }
+
+  set_volume(volume) {
+    return this.module._wasm_audio_set_volume(this.media_player_ptr, volume);
+  }
+
+  get_channel() {
+    return this.module._wasm_audio_get_channel(this.media_player_ptr);
+  }
+
+  set_channel(rate) {
+    return this.module._wasm_audio_set_channel(this.media_player_ptr, rate);
+  }
+
+  get_delay() {
+    return this.module._wasm_audio_get_delay(this.media_player_ptr);
+  }
+
+  set_delay(rate) {
+    return this.module._wasm_audio_set_delay(this.media_player_ptr, rate);
+  }
+
+  get_role() {
+    return this.module._wasm_media_player_get_role(this.media_player_ptr);
+  }
+
+  set_role(role) {
+    return this.module._wasm_media_player_set_role(this.media_player_ptr, role);
+  }
+}
+
+
+// Encapsulates libvlc_media_t
+export class Media {
+  constructor(module, path, raw_ptr) {
+    if (raw_ptr != null) {
+      this.module = module;
+      this.media_ptr = raw_ptr;
+      return;
+    }
+
+    if (typeof path != 'string') {
+      throw new Error("Tried to create Media with invalid value");
+    }
+
+    this.module = module;
+
+    let path_ptr = module.allocateUTF8(path)
+    this.media_ptr = module._wasm_media_new_path(path_ptr);
+    module._free(path_ptr);
+
+    if (this.media_ptr == 0) {
+      // TODO - give more context
+      throw new Error(`Could not create media from path {path}`);
+    }
+  }
+
+  release() {
+    this.module._wasm_media_release(this.media_ptr);
+    this.media_ptr = 0;
+  }
+}
diff --git a/assets/module-loader.js b/lib/module-loader.js
similarity index 59%
rename from assets/module-loader.js
rename to lib/module-loader.js
index f536ca5b08c07b8cbc87face67879f8d254d2309..1e89cc4e066b681e8e7e6354643e95d3ec92c07e 100644
--- a/assets/module-loader.js
+++ b/lib/module-loader.js
@@ -2,9 +2,20 @@ var statusElement = document.getElementById('status');
 var progressElement = document.getElementById('progress');
 var spinnerElement = document.getElementById('spinner');
 
-var Module = {
+var VlcModuleExt = {
   preRun: [],
-  postRun: [],
+  postRun: [ function() {
+    // This should run after the wasm module is instantiated
+    // before, the Pthread object won't be available
+    VlcModuleExt.PThread.receiveObjectTransfer = function (data) {
+      // Transfer messages from worker threads to the main window.
+      let event = new CustomEvent('worker_message', {
+        detail: data.msg
+      });
+      window.dispatchEvent(event);
+    };
+  }],
+
   print: (function() {
     var element = document.getElementById('output');
     if (element) element.value = ''; // clear browser cache
@@ -26,22 +37,39 @@ var Module = {
     if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
     console.error(text);
   },
+
   canvas: (function() {
     var canvas = document.getElementById('canvas')
+    var overlay = document.getElementById('overlay')
     // As a default initial behavior, pop up an alert when webgl context is lost. To make your
     // application robust, you may want to override this behavior before shipping!
     // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
-    canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false)
+    canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+    overlay.addEventListener('click', (event) => {
+      window.on_overlay_click(overlay, event);
+    });
+
+    // Create a global window.display_overlay variable to track whether
+    // the UI should be visible - see update_overlay function
+    window.display_overlay = true,
+    overlay.addEventListener('mouseenter', e => {
+      window.display_overlay = true;
+    });
+    overlay.addEventListener('mouseleave', e => {
+      window.display_overlay = false;
+    });
+
     return canvas;
   })(),
   setStatus: function(text) {
-    if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
-    if (text === Module.setStatus.last.text) return;
+    if (!VlcModuleExt.setStatus.last) VlcModuleExt.setStatus.last = { time: Date.now(), text: '' };
+    if (text === VlcModuleExt.setStatus.last.text) return;
     var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
     var now = Date.now();
-    if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
-    Module.setStatus.last.time = now;
-    Module.setStatus.last.text = text;
+    if (m && now - VlcModuleExt.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
+    VlcModuleExt.setStatus.last.time = now;
+    VlcModuleExt.setStatus.last.text = text;
     if (m) {
       text = m[1];
       progressElement.value = parseInt(m[2])*100;
@@ -59,15 +87,6 @@ var Module = {
   totalDependencies: 0,
   monitorRunDependencies: function(left) {
     this.totalDependencies = Math.max(this.totalDependencies, left);
-    Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+    VlcModuleExt.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
   }
 };
-Module.setStatus('Downloading...');
-window.onerror = function(event) {
-  // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
-  Module.setStatus('Exception thrown, see JavaScript console');
-  spinnerElement.style.display = 'none';
-  Module.setStatus = function(text) {
-    if (text) Module.printErr('[post-exception status] ' + text);
-  };
-};
\ No newline at end of file
diff --git a/lib/overlay.js b/lib/overlay.js
new file mode 100644
index 0000000000000000000000000000000000000000..7e0d627386bc27abef1d9df4c113ddcfa533d13b
--- /dev/null
+++ b/lib/overlay.js
@@ -0,0 +1,230 @@
+// Functions to manage both the layout and user inputs of the video overlay
+// It's very artisanal and would usually be done with a framework like
+// React, but it's enough for the needs of the project.
+
+const PROGRESS_BAR_COLOR = "#2a81d4";
+const PROGRESS_BAR_BG_COLOR = "#2d2d2d";
+const VOLUME_BAR_COLOR = "#4ad24a";
+const VOLUME_BAR_MUTE_COLOR = "#4d704e";
+const VOLUME_BAR_BG_COLOR = "#2d2d2d";
+
+const BUTTON_BG_COLOR = "#464646";
+const GENERAL_BG_COLOR = "#353535";
+const TEXT_COLOR = "#8d8d8c";
+
+const MENU_BAR_HEIGHT = 30;
+const BUTTON_SIZE = 20;
+const GAP_SIZE = 5;
+
+function get_layout(canvas) {
+  const VOLUME_BAR_WIDTH = 100;
+  const VOLUME_BAR_HEIGHT = 20;
+
+  const PROGRESS_TEXT_WIDTH = 80;
+
+  const PROGRESS_BAR_WIDTH = canvas.width -
+  (GAP_SIZE + BUTTON_SIZE + GAP_SIZE + GAP_SIZE + BUTTON_SIZE + GAP_SIZE + PROGRESS_TEXT_WIDTH + GAP_SIZE + VOLUME_BAR_WIDTH + GAP_SIZE);
+  const PROGRESS_BAR_HEIGHT = 10;
+
+  const PLAY_BUTTON_X = GAP_SIZE;
+  const PROGRESS_BAR_X = PLAY_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
+  const PROGRESS_TEXT_X = PROGRESS_BAR_X + PROGRESS_BAR_WIDTH + GAP_SIZE;
+  const VOLUME_BUTTON_X = PROGRESS_TEXT_X + PROGRESS_TEXT_WIDTH + GAP_SIZE;
+  const VOLUME_BAR_X = VOLUME_BUTTON_X + BUTTON_SIZE + GAP_SIZE;
+
+  return {
+    VOLUME_BAR_WIDTH,
+    VOLUME_BAR_HEIGHT,
+    PROGRESS_TEXT_WIDTH,
+    PROGRESS_BAR_WIDTH,
+    PROGRESS_BAR_HEIGHT,
+    PLAY_BUTTON_X,
+    PROGRESS_BAR_X,
+    PROGRESS_TEXT_X,
+    VOLUME_BUTTON_X,
+    VOLUME_BAR_X,
+  };
+}
+
+const central_play_icon = new Image(50, 50);
+central_play_icon.src = "vlc/modules/gui/qt/pixmaps/play_button.svg";
+
+const play_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
+play_button.src = "vlc/modules/gui/qt/pixmaps/play.png";
+const pause_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
+pause_button.src = "vlc/modules/gui/qt/pixmaps/pause.png";
+
+const volume_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
+volume_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-medium.png";
+const muted_button = new Image(BUTTON_SIZE, BUTTON_SIZE);
+muted_button.src = "vlc/modules/gui/qt/pixmaps/toolbar/volume-muted.png";
+
+// Apply underlying changes (eg video is paused) to the displayed UI
+export function update_overlay(overlay) {
+  const ctx = overlay.getContext("2d");
+  const media_player = window.media_player;
+  if (media_player == null) {
+    return;
+  }
+
+  const {
+    VOLUME_BAR_WIDTH,
+    VOLUME_BAR_HEIGHT,
+    PROGRESS_TEXT_WIDTH,
+    PROGRESS_BAR_WIDTH,
+    PROGRESS_BAR_HEIGHT,
+    PLAY_BUTTON_X,
+    PROGRESS_BAR_X,
+    PROGRESS_TEXT_X,
+    VOLUME_BUTTON_X,
+    VOLUME_BAR_X,
+  } = get_layout(overlay);
+
+  ctx.save();
+  ctx.clearRect(0, 0, overlay.width, overlay.height);
+
+  let is_paused = !media_player.is_playing();
+
+  if (is_paused) {
+    ctx.drawImage(
+      central_play_icon,
+      overlay.width / 2 - central_play_icon.width / 2,
+      overlay.height / 2 - central_play_icon.height / 2,
+      central_play_icon.width,
+      central_play_icon.height,
+    );
+  }
+
+  if (window.display_overlay) {
+    // -- PAINT BACKGROUND --
+    ctx.fillStyle = GENERAL_BG_COLOR;
+    ctx.fillRect(
+      0, overlay.height - MENU_BAR_HEIGHT,
+      overlay.width, MENU_BAR_HEIGHT
+    );
+
+    let y = overlay.height - MENU_BAR_HEIGHT;
+
+    // -- DRAW PLAY/PAUSE BUTTON --
+    ctx.drawImage(is_paused ? play_button : pause_button,
+      GAP_SIZE, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
+      BUTTON_SIZE, BUTTON_SIZE
+    );
+
+    // -- DRAW PROGRESS BAR --
+    let position = media_player.get_position();
+
+    ctx.fillStyle = PROGRESS_BAR_BG_COLOR;
+    ctx.fillRect(
+      PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
+      PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT
+    );
+    ctx.fillStyle = PROGRESS_BAR_COLOR;
+    ctx.fillRect(
+      PROGRESS_BAR_X, y + (MENU_BAR_HEIGHT - PROGRESS_BAR_HEIGHT) / 2,
+      PROGRESS_BAR_WIDTH * position, PROGRESS_BAR_HEIGHT
+    );
+
+    // DRAW PROGRESS TEXT (eg: 12:31 / 15:00) --
+    let time = media_player.get_time();
+    let seconds = Math.trunc(time / 1000);
+    let minutes = Math.trunc(seconds / 60);
+    seconds = seconds % 60;
+    let max_time = media_player.get_length();
+    let max_seconds = Math.trunc(max_time / 1000);
+    let max_minutes = Math.trunc(max_seconds / 60);
+    max_seconds = max_seconds % 60;
+
+    ctx.textAlign = "right";
+    ctx.textBaseline = "middle";
+    ctx.fillStyle = TEXT_COLOR;
+    ctx.fillText(
+      `${minutes}:${seconds} / ${max_minutes}:${max_seconds}`,
+      PROGRESS_TEXT_X + PROGRESS_TEXT_WIDTH, y + MENU_BAR_HEIGHT / 2,
+    );
+
+    // -- DRAW VOLUME/MUTE BUTTON --
+    let is_muted = media_player.get_mute();
+    ctx.drawImage(is_muted ? muted_button : volume_button,
+      VOLUME_BUTTON_X, y + (MENU_BAR_HEIGHT - BUTTON_SIZE) / 2,
+      BUTTON_SIZE, BUTTON_SIZE
+    );
+
+    // -- DRAW VOLUME BAR --
+    let volume = media_player.get_volume() / 100;
+
+    ctx.fillStyle = VOLUME_BAR_BG_COLOR;
+    ctx.fillRect(
+      VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
+      VOLUME_BAR_WIDTH, VOLUME_BAR_HEIGHT
+    );
+    ctx.fillStyle = is_muted ? VOLUME_BAR_MUTE_COLOR : VOLUME_BAR_COLOR;
+    ctx.fillRect(
+      VOLUME_BAR_X, y + (MENU_BAR_HEIGHT - VOLUME_BAR_HEIGHT) / 2,
+      VOLUME_BAR_WIDTH * volume, VOLUME_BAR_HEIGHT
+    );
+  }
+
+  ctx.restore();
+}
+
+export function on_overlay_click(overlay, mouse_event) {
+  const ctx = overlay.getContext("2d");
+  const media_player = window.media_player;
+
+  if (media_player == null) {
+    // Video isn't loaded yet
+    return;
+  }
+
+  const {
+    VOLUME_BAR_WIDTH,
+    VOLUME_BAR_HEIGHT,
+    PROGRESS_BAR_WIDTH,
+    PROGRESS_BAR_HEIGHT,
+    PLAY_BUTTON_X,
+    PROGRESS_BAR_X,
+    VOLUME_BUTTON_X,
+    VOLUME_BAR_X,
+  } = get_layout(overlay);
+
+  let canvas_rect = mouse_event.target.getBoundingClientRect();
+  let x = mouse_event.clientX - canvas_rect.left;
+  let y = mouse_event.clientY - canvas_rect.top;
+
+  // User clicked in menu bar
+  if (y > overlay.height - MENU_BAR_HEIGHT) {
+    // User clicked on play/pause button
+    if (x > PLAY_BUTTON_X && x < PLAY_BUTTON_X + BUTTON_SIZE) {
+      media_player.toggle_play();
+      update_overlay(overlay);
+    }
+
+    // User clicked on progress bar
+    if (x > PROGRESS_BAR_X && x < PROGRESS_BAR_X + PROGRESS_BAR_WIDTH) {
+      let progress = (x - PROGRESS_BAR_X) / PROGRESS_BAR_WIDTH;
+      media_player.set_position(progress);
+      update_overlay(overlay);
+    }
+
+    // User clicked on volume/mute button
+    if (x > VOLUME_BUTTON_X && x < VOLUME_BUTTON_X + BUTTON_SIZE) {
+      media_player.toggle_mute();
+      update_overlay(overlay);
+    }
+
+    // User clicked on volume bar
+    if (x > VOLUME_BAR_X && x < VOLUME_BAR_X + VOLUME_BAR_WIDTH) {
+      let new_volume = (x - VOLUME_BAR_X) / VOLUME_BAR_WIDTH;
+      media_player.set_volume(new_volume * 100);
+      // Unmute
+      media_player.set_mute(0);
+      update_overlay(overlay);
+    }
+  }
+
+  // User clicked outside the menu bar
+  else {
+    media_player.toggle_play();
+  }
+}
diff --git a/lib/wasm-imports.js b/lib/wasm-imports.js
new file mode 100644
index 0000000000000000000000000000000000000000..a3c7df201e76a5c32356f06030db598f63daedfe
--- /dev/null
+++ b/lib/wasm-imports.js
@@ -0,0 +1,21 @@
+// Functions injected into the wasm binary, can be
+// called from C/C++ code.
+// The top level of this JS file is executed at compile time.
+// Functions bodies are copy-pasted into 'experimental.js'
+// (unless they're culled from dead code elimination)
+
+mergeInto(LibraryManager.library, {
+  // Apply underlying changes (eg video is paused) to the displayed UI
+  update_overlay: function() {
+    const overlay = document.getElementById("overlay");
+    update_overlay(overlay);
+  },
+
+  // Worker functions - These are intended to be called from threads
+  // They can't access the browser APIs directly, so they send messages
+
+  // TODO - This essentially sends one message per frame; might be bad for perf
+  on_position_changed: function() {
+    postMessage({ cmd: "objectTransfer", msg: "on_position_changed" });
+  }
+});
diff --git a/main.c b/main.c
index c39c1b1116f1f39a551acbdef5e635e9d77f2fd3..6c04056a2ec85a9774f48f848be273a5dce108bf 100644
--- a/main.c
+++ b/main.c
@@ -1,18 +1,20 @@
 #include <stdio.h>
 #include <vlc/vlc.h>
 #include <vlc_common.h>
+#include <assert.h>
 #include <errno.h>
 
 #include <emscripten.h>
 #include <emscripten/html5.h>
 
 libvlc_media_player_t *mp;
-libvlc_instance_t *libvlc; 
+libvlc_instance_t *libvlc;
 libvlc_time_t t = -1;
-char flag = 1;
 
 static void iter()
 {
+  if (!mp)
+    return;
 	if (libvlc_media_player_get_time(mp) == t) {
 		// when enable, the js does not respond.
 		//libvlc_media_player_release( mp );
@@ -22,72 +24,57 @@ static void iter()
 	t = libvlc_media_player_get_time(mp);
 }
 
-static EM_BOOL play_pause_handler(int eventType, const EmscriptenMouseEvent *e, void *userData)
-{
-  (void) e;
-  (void) userData; // To use when mp won't be a global.
-  if (eventType == EMSCRIPTEN_EVENT_CLICK)
-  {
-    if (flag == 1)
-      {
-	libvlc_media_player_play(mp);
-	flag = 0;
-      }
-    // Don't do that until you implement a/v synchro.
-    // libvlc_media_player_pause(mp);
-    // flag = 1;
-  }
-  return 0;
+void EMSCRIPTEN_KEEPALIVE set_global_media_player(libvlc_media_player_t *media_player) {
+  mp = media_player;
 }
 
+extern void update_overlay();
+extern void on_position_changed(const libvlc_event_t *p_event, void *p_data);
+
 int main() {
 
 	/* We don't want to the main thread stop even if the main function exit.
 	 * If this thread stop, all proxyfied functions wont be called.
 	 */
   EM_ASM(Module['noExitRuntime']=true);
-  emscripten_set_element_css_size("#canvas", 720, 540);
-  libvlc_media_t *m;
   char const *vlc_argv[] = {
 			    "-vvv",
 			    "--no-spu",
 			    "--no-osd",
+			    "--aout=emworklet_audio",
 			    "-Idummy",
 			    "--ignore-config",
   };
-  
+
   libvlc = libvlc_new( ARRAY_SIZE( vlc_argv ), vlc_argv );
-    
   if (libvlc == NULL)
     {
       fprintf( stderr, "unable to create libvlc instance" );
       return -1;
     }
-  m = libvlc_media_new_path( libvlc, "./BigBuckBunny.mp4" );
-  
-  if (m == NULL)
-    {
-      fprintf(stderr, "unable to create media");
-      return -1;
-    }
-  mp = libvlc_media_player_new_from_media( m );
-  if (mp == NULL)
-    {
-      fprintf(stderr, "unable to create media player");
-      return -1;
-    }
-  
-  libvlc_media_release( m );
-  m = libvlc_media_player_get_media(mp);
-  // libvlc_audio_set_volume (mp, 100);
-  // int res = libvlc_media_player_play (mp);
-  /*	if (res != 0) {	
-        fprintf( stderr, "unable to play media" );
-	return -1;
-	}
-  */
-  emscripten_set_click_callback("#canvas", 0, 1, play_pause_handler);
+
   emscripten_set_main_loop(iter, 1, 1);
-  
+
   return 0;
 }
+
+// Used to make sure the UI (progress bar, play/pause button, etc) is
+// updated as the video is read.
+void EMSCRIPTEN_KEEPALIVE attach_update_events(libvlc_media_player_t *media_player) {
+  libvlc_event_manager_t* event_manager = libvlc_media_player_event_manager(media_player);
+  int res;
+  res = libvlc_event_attach(
+    event_manager,
+    libvlc_MediaPlayerPositionChanged,
+    on_position_changed,
+    NULL
+  );
+  assert(res == 0);
+  res = libvlc_event_attach(
+    event_manager,
+    libvlc_MediaPlayerPaused,
+    on_position_changed,
+    NULL
+  );
+  assert(res == 0);
+}
diff --git a/vlc.html b/vlc.html
index 5042212616bdaf22f91d67419f1f4b21d91caee1..29e1ebe482a00a825142955dd29edf0ddc456fd5 100644
--- a/vlc.html
+++ b/vlc.html
@@ -11,12 +11,24 @@
         body {
             background-color: #343a40;
         }
-        canvas.emscripten {
+        #canvas {
             border:0 !important;
             display: flex;
             margin: 0 auto;
             background-color: #000;
         }
+        #overlay {
+            border:0 !important;
+            display: flex;
+            margin: 0 auto;
+        }
+        #stack {
+            display: grid;
+        }
+        #stack > canvas {
+            grid-column: 1;
+            grid-row: 1;
+        }
         #spinner {
             display: none;
         }
@@ -57,22 +69,76 @@
 </head>
 <body>
     <div class="emscripten_border">
-    <div class="emscripten">   
+    <div class="emscripten">
         <div class="banner">
             <img src="./assets/emscripten.svg" id ="em_logo">
             <div class="vlc_head">
                 <img src="./assets/VLC_Icon.svg" id="logo">
                 <span id="point">.</span>
-                <span id="js">JS</span> 
+                <span id="js">JS</span>
             </div>
         </div>
         <progress id="progress" value="0" max="100"></progress>
         <div class="spinner" id='spinner'></div>
         <div class="emscripten" id="status">Downloading...</div>
     </div>
-        <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
     </div>
-    <script type="text/javascript" src="./assets/module-loader.js"></script>
-    <script async type="text/javascript" src="./experimental.js"></script>
+
+    <div id="stack">
+        <canvas
+          class="emscripten" id="canvas"
+          oncontextmenu="event.preventDefault()" tabindex=-1
+          width=1280 height=720
+        ></canvas>
+        <canvas
+          class="emscripten hidden" id="overlay"
+          oncontextmenu="event.preventDefault()" tabindex=-2
+          width=1280 height=720
+        ></canvas>
+    </div>
+
+    <script src="./lib/module-loader.js"></script>
+    <script src="./experimental.js"></script>
+
+    <script type="module">
+      import { update_overlay, on_overlay_click } from "./lib/overlay.js";
+      import { MediaPlayer } from "./lib/libvlc.js";
+
+      // VlcModule is a function generated by emscripten in experimental.js,
+      // that loads the wasm file and generates a module object from it.
+      // VlcModuleExt is an object defined in 'lib/module-loader.js' with a
+      // bunch of options; also, all the fields of VlcModuleExt are added to
+      // the returned Module.
+      const Module = await VlcModule(VlcModuleExt);
+
+      window.onerror = function(event) {
+        // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+        Module.setStatus('Exception thrown, see JavaScript console');
+        spinnerElement.style.display = 'none';
+        Module.setStatus = function(text) {
+          if (text) Module.printErr('[post-exception status] ' + text);
+        };
+      };
+
+      const overlay = document.getElementById("overlay");
+      const media_player = new MediaPlayer(Module, "./samples/BigBuckBunny.mp4");
+
+      // FIXME
+      Module._set_global_media_player(media_player.media_player_ptr);
+
+      window.media_player = media_player;
+      window.on_overlay_click = on_overlay_click;
+      window.update_overlay = update_overlay;
+
+      update_overlay(overlay);
+
+      addEventListener('worker_message', function(msg) {
+        if (msg.detail === "on_position_changed") {
+          update_overlay(overlay);
+        }
+      });
+    </script>
+
+
 </body>
 </html>
diff --git a/vlc_patches/0001-configure-improve-testing-unsupported-GL-functions-f.patch b/vlc_patches/0001-configure-improve-testing-unsupported-GL-functions-f.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d5e39a521f17e0ff956a60f1f4e2df45e53ce0a7
--- /dev/null
+++ b/vlc_patches/0001-configure-improve-testing-unsupported-GL-functions-f.patch
@@ -0,0 +1,42 @@
+From b848ce9dd721810e11a1566d9dfdea3c34c13ace Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Tue, 27 Apr 2021 15:34:23 +0200
+Subject: [PATCH 1/1] configure: improve testing unsupported GL functions for
+ emscripten
+
+The build system assumes OpenGL functions are implemented if the headers are defined.
+Which is wrong, so we need to disable the HAVE_GL marker, if linking fails.
+
+we use AM_LINK_IFELSE() because AC_COMPILE will add the "-c" option, and thus
+in wasm-emscripten this function will appear supported even if it is not.
+---
+ configure.ac | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 374ce00cc7..81abc3fcf0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -3311,13 +3311,16 @@ PKG_CHECK_MODULES([GL], [gl], [
+   have_gl="yes"
+ ], [
+   AC_MSG_CHECKING([for OpenGL])
+-  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
++  AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ #ifdef _WIN32
+ # include <GL/glew.h>
+ #endif
+ #include <GL/gl.h>
+-]], [
+-    [int t0 = GL_TEXTURE0;]])
++]], [[
++	int t0 = GL_TEXTURE0;
++	// glColorMaterial is unavailable in webgl, and emscripten
++	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
++	]])
+   ], [
+     GL_CFLAGS=""
+     have_gl="yes"
+-- 
+2.32.0
+
diff --git a/vlc_patches/0001-contrib-add-emscripten-target.patch b/vlc_patches/0001-contrib-add-emscripten-target.patch
deleted file mode 100644
index f42ffd4276ff8a189c8122f063dad14784a3effe..0000000000000000000000000000000000000000
--- a/vlc_patches/0001-contrib-add-emscripten-target.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 89483da629c05701ac2ff8a157a67e031e5e471f Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Mon, 30 Dec 2019 13:55:23 +0100
-Subject: [PATCH 1/1] contrib: add emscripten target
-
-set toolchain variables in bootstrap and main.mak
-
-Co-Author: Etienne Brateau <etienne.brateau@gmail.com>
----
- contrib/bootstrap    | 3 +++
- contrib/src/main.mak | 9 +++++++++
- 2 files changed, 12 insertions(+)
-
-diff --git a/contrib/bootstrap b/contrib/bootstrap
-index 01a234e55c..b6224bae9d 100755
---- a/contrib/bootstrap
-+++ b/contrib/bootstrap
-@@ -348,6 +348,9 @@ case "${OS}" in
- 	*nacl*)
- 		add_make_enabled "HAVE_NACL"
- 		;;
-+	*emscripten*)
-+	        add_make_enabled "HAVE_EMSCRIPTEN"
-+		;;
- esac
- 
- #
-diff --git a/contrib/src/main.mak b/contrib/src/main.mak
-index 5e5846de97..897020517e 100644
---- a/contrib/src/main.mak
-+++ b/contrib/src/main.mak
-@@ -133,6 +133,15 @@ EXTRA_CFLAGS += -fno-stack-check
- XCODE_FLAGS += OTHER_CFLAGS=-fno-stack-check
- endif
- 
-+ifdef HAVE_EMSCRIPTEN
-+CC := emcc
-+CXX := em++
-+LD := emcc
-+AR := emar
-+RANLIB := emranlib
-+CFLAGS="-pthread"
-+endif
-+
- ifdef HAVE_MACOSX
- EXTRA_CXXFLAGS += -stdlib=libc++
- ifeq ($(ARCH),x86_64)
--- 
-2.24.1
-
diff --git a/vlc_patches/0001-modules-disable-libvlc_json-and-ytbdl-vlc.js-17.patch b/vlc_patches/0001-modules-disable-libvlc_json-and-ytbdl-vlc.js-17.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6a81d35083ff2d06df598173bd600e0c21b410e3
--- /dev/null
+++ b/vlc_patches/0001-modules-disable-libvlc_json-and-ytbdl-vlc.js-17.patch
@@ -0,0 +1,37 @@
+From b67a4befac19e46f48e7955b5475dca89e8c9d52 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Fri, 16 Apr 2021 11:32:33 +0200
+Subject: [PATCH 1/1] modules: disable libvlc_json and ytbdl vlc.js#17
+
+The libjson library is not linkable with emsdk v2.0.17,
+this commit should be reverted when the ticket is resolved.
+---
+ modules/demux/Makefile.am | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
+index 48f5d7a97d..dbfe923650 100644
+--- a/modules/demux/Makefile.am
++++ b/modules/demux/Makefile.am
+@@ -529,9 +529,11 @@ libytdl_plugin_la_SOURCES = demux/ytdl.c
+ libytdl_plugin_la_LIBADD = libvlc_json.la
+ if !HAVE_WIN32
+ if !HAVE_ANDROID
++if !HAVE_EMSCRIPTEN
+ demux_LTLIBRARIES += libytdl_plugin.la
+ endif
+ endif
++endif
+ 
+ libnoseek_plugin_la_SOURCES = demux/filter/noseek.c
+ demux_LTLIBRARIES += libnoseek_plugin.la
+@@ -549,4 +551,6 @@ libvlc_json_la_SOURCES = \
+ libvlc_json_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/demux/json
+ libvlc_json_la_LIBADD = $(LTLIBVLCCORE) ../compat/libcompat.la $(LIBM)
+ libvlc_json_la_LDFLAGS = -static
++if !HAVE_EMSCRIPTEN
+ noinst_LTLIBRARIES += libvlc_json.la
++endif
+-- 
+2.32.0
+
diff --git a/vlc_patches/0002-contrib-add-ffmpeg-configuration-options-for-wasm-em.patch b/vlc_patches/0002-contrib-add-ffmpeg-configuration-options-for-wasm-em.patch
deleted file mode 100644
index cbcbf5ddfcd1fab13a8391de8a9b7d444447ad05..0000000000000000000000000000000000000000
--- a/vlc_patches/0002-contrib-add-ffmpeg-configuration-options-for-wasm-em.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-From 4a82c06361880c86123934b6dfb54263e0154051 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Wed, 4 Sep 2019 19:35:17 +0200
-Subject: [PATCH 01/15] contrib: add ffmpeg configuration options for
- wasm-emscripten
-
-We need to specify the pthread flag for compilation and linking, otherwise tests will fail.
----
- ...001-configure-add-emscripten-support.patch | 33 +++++++++++++++++++
- contrib/src/ffmpeg/rules.mak                  |  6 ++++
- 2 files changed, 39 insertions(+)
- create mode 100644 contrib/src/ffmpeg/0001-configure-add-emscripten-support.patch
-
-diff --git a/contrib/src/ffmpeg/0001-configure-add-emscripten-support.patch b/contrib/src/ffmpeg/0001-configure-add-emscripten-support.patch
-new file mode 100644
-index 0000000000..cb8471c90e
---- /dev/null
-+++ b/contrib/src/ffmpeg/0001-configure-add-emscripten-support.patch
-@@ -0,0 +1,33 @@
-+From da7782c47f1f3d84eefb4ccce1c95e40d3b65fde Mon Sep 17 00:00:00 2001
-+From: Mehdi Sabwat <mehdisabwat@gmail.com>
-+Date: Tue, 13 Aug 2019 21:14:56 +0200
-+Subject: [PATCH 1/1] configure: add emscripten support
-+
-+---
-+ configure | 3 +++
-+ 1 file changed, 3 insertions(+)
-+
-+diff --git a/configure b/configure
-+index 7cea9d4d73..bafcbc87fc 100755
-+--- a/configure
-++++ b/configure
-+@@ -4239,6 +4239,7 @@ fi
-+ exesuf() {
-+     case $1 in
-+         mingw32*|mingw64*|win32|win64|cygwin*|*-dos|freedos|opendos|os/2*|symbian) echo .exe ;;
-++        emscripten) echo .js ;;
-+     esac
-+ }
-+ 
-+@@ -5428,6 +5429,8 @@ case $target_os in
-+         ;;
-+     minix)
-+         ;;
-++    emscripten)
-++        ;;
-+     none)
-+         ;;
-+     *)
-+-- 
-+2.23.0
-+
-diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak
-index 1ba04616e5..e2ec5f3cd3 100644
---- a/contrib/src/ffmpeg/rules.mak
-+++ b/contrib/src/ffmpeg/rules.mak
-@@ -221,6 +221,11 @@ ifdef HAVE_NACL
- FFMPEGCONF+=--disable-inline-asm --disable-asm --target-os=linux
- endif
- 
-+ifdef HAVE_EMSCRIPTEN
-+FFMPEGCONF+=--target-os=emscripten --arch=wasm32 --ranlib=emranlib \
-+	    --extra-ldflags="-pthread" --extra-ldexeflags="-pthread"
-+endif
-+
- # Build
- PKGS += ffmpeg
- ifeq ($(call need_pkg,"libavcodec >= $(FFMPEG_LAVC_MIN) libavformat >= 53.21.0 libswscale"),)
-@@ -248,6 +253,7 @@ endif
- ifdef USE_LIBAV
- 	$(APPLY) $(SRC)/ffmpeg/libav_gsm.patch
- endif
-+	$(APPLY) $(SRC)/ffmpeg/0001-configure-add-emscripten-support.patch
- 	$(MOVE)
- 
- .ffmpeg: ffmpeg
--- 
-2.23.0
-
diff --git a/vlc_patches/0003-contrib-delete-empty-variable.patch b/vlc_patches/0003-contrib-delete-empty-variable.patch
deleted file mode 100644
index 05c73b66a356f446dc4107db040ee480ed8352e8..0000000000000000000000000000000000000000
--- a/vlc_patches/0003-contrib-delete-empty-variable.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From ba9735f71e72cd3c7b57b1558a69c9f111316f65 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Tue, 31 Dec 2019 12:02:20 +0100
-Subject: [PATCH 1/1] contrib: delete empty variable
-
-As a fwp on c29409d1a742e65b6b2f3c95702196ff9ab1570c this commit fixes an issue
-on platforms that are not listed.
----
- contrib/src/main.mak | 1 -
- 1 file changed, 1 deletion(-)
-
-diff --git a/contrib/src/main.mak b/contrib/src/main.mak
-index 897020517e..a0469fb3e0 100644
---- a/contrib/src/main.mak
-+++ b/contrib/src/main.mak
-@@ -646,7 +646,6 @@ ifdef HAVE_CROSS_COMPILE
- 	echo "set(PKG_CONFIG_EXECUTABLE $(PKG_CONFIG))" >> $@
- endif
- 
--MESON_SYSTEM_NAME =
- ifdef HAVE_WIN32
- 	MESON_SYSTEM_NAME = windows
- else
--- 
-2.24.1
-
diff --git a/vlc_patches/0006-configure-Create-a-target-for-emscripten-in-the-conf.patch b/vlc_patches/0006-configure-Create-a-target-for-emscripten-in-the-conf.patch
deleted file mode 100644
index 3d09a3afc2f87d5470b18aceace95bcd55c80673..0000000000000000000000000000000000000000
--- a/vlc_patches/0006-configure-Create-a-target-for-emscripten-in-the-conf.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 3468bd741f19ff68d1017c374fdec6955bb5454e Mon Sep 17 00:00:00 2001
-From: Etienne Brateau <etienne.brateau@gmail.com>
-Date: Thu, 6 Jul 2017 14:50:28 +0200
-Subject: [PATCH 06/15] configure: Create a target for emscripten in the
- configure.ac
-
----
- configure.ac | 15 +++++++++++++++
- 1 file changed, 15 insertions(+)
-
-diff --git a/configure.ac b/configure.ac
-index 210b4ca537..c9d04253cd 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -322,6 +322,19 @@ case "${host_os}" in
-     AC_LIBOBJ([recvmsg])
-     AC_LIBOBJ([sendmsg])
-     ;;
-+  *emscripten*)
-+    SYS=emscripten
-+    CC=emcc
-+    LD=emcc
-+    LDSHARED=emcc
-+    NM=llvm-nm
-+    CPP=emcc
-+    CXX=em++
-+    AR=emar
-+    RANLIB=emranlib
-+    CFLAGS="${CFLAGS} -D__NEED_ssize_t -pthread"
-+    CXXFLAGS="${CXXFLAGS}"
-+  ;;
-   *)
-     SYS="${host_os}"
-     ;;
-@@ -388,6 +401,8 @@ AM_COND_IF([HAVE_X86ASM], [
- AC_SUBST([X86ASMFLAGS])
- AC_SUBST([X86ASMDEFS])
- 
-+AM_CONDITIONAL([HAVE_EMSCRIPTEN], [test "${SYS}" = "emscripten"])
-+
- dnl
- dnl Sadly autoconf does not think about testing foo.exe when ask to test
- dnl for program foo on win32
--- 
-2.23.0
-
diff --git a/vlc_patches/0007-core-initial-core-build-for-emscripten-based-on-POSI.patch b/vlc_patches/0007-core-initial-core-build-for-emscripten-based-on-POSI.patch
deleted file mode 100644
index 8496156418914a7c854bd158ccf68415452ab23f..0000000000000000000000000000000000000000
--- a/vlc_patches/0007-core-initial-core-build-for-emscripten-based-on-POSI.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 747c6207acf654665d23625537b962152922c8c5 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Mon, 24 Feb 2020 14:08:02 +0100
-Subject: [PATCH 1/1] core: initial core build for emscripten, based on POSIX
-
-posix/sort.c won't be added because qsort_r is not supported.
----
- src/Makefile.am | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index bf91159fcc..474ddcdd63 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -437,6 +437,17 @@ libvlccore_la_SOURCES += \
- 	posix/timer.c
- endif
- 
-+if HAVE_EMSCRIPTEN
-+libvlccore_la_SOURCES += \
-+	posix/thread.c \
-+	posix/getaddrinfo.c \
-+	posix/error.c \
-+	posix/dirs.c \
-+	posix/filesystem.c \
-+	posix/specific.c \
-+	posix/timer.c
-+endif
-+
- if HAVE_DARWIN
- libvlccore_la_SOURCES += \
- 	darwin/error.c \
-@@ -481,6 +492,7 @@ if !HAVE_LINUX
- libvlccore_la_SOURCES += posix/wait.c
- endif
- if !HAVE_ANDROID
-+if !HAVE_EMSCRIPTEN
- libvlccore_la_SOURCES += posix/sort.c
- if !HAVE_DARWIN
- libvlccore_la_SOURCES += \
-@@ -503,6 +515,7 @@ endif
- endif
- endif
- endif
-+endif
- 
- if ENABLE_SOUT
- libvlccore_la_SOURCES += \
--- 
-2.25.0
-
diff --git a/vlc_patches/0008-compat-add-sigwait-support-for-emscripten.patch b/vlc_patches/0008-compat-add-sigwait-support-for-emscripten.patch
deleted file mode 100644
index 180433c4e5c1d1e61d314c85617419b82ff4f510..0000000000000000000000000000000000000000
--- a/vlc_patches/0008-compat-add-sigwait-support-for-emscripten.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From e06b3ac370e84480e7d3c6d9e8184c27fc2d0bf5 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Tue, 17 Sep 2019 18:59:43 +0200
-Subject: [PATCH 08/15] compat: add sigwait support for emscripten
-
----
- compat/sigwait.c | 4 ++--
- configure.ac     | 1 +
- 2 files changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/compat/sigwait.c b/compat/sigwait.c
-index e5a082d2d6..950579cc8b 100644
---- a/compat/sigwait.c
-+++ b/compat/sigwait.c
-@@ -24,8 +24,8 @@
- # include <config.h>
- #endif
- 
--#ifdef __native_client__
--/* NaCl has no working sigwait, but SIGPIPE, for which vlc uses sigwait
-+#if defined(__native_client__) || defined(__EMSCRIPTEN__) 
-+/* NaCl and Emscripten have no working sigwait, but SIGPIPE, for which vlc uses sigwait
-  * currently, is never generated in NaCl. So for SIGPIPE it's safe to instantly
-  * return, for all others run into an assertion. */
- 
-diff --git a/configure.ac b/configure.ac
-index c9d04253cd..e5298e6f7a 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -324,6 +324,7 @@ case "${host_os}" in
-     ;;
-   *emscripten*)
-     SYS=emscripten
-+    AC_LIBOBJ([sigwait])
-     CC=emcc
-     LD=emcc
-     LDSHARED=emcc
--- 
-2.23.0
-
diff --git a/vlc_patches/0009-compat-add-clock_nanosleep-support.patch b/vlc_patches/0009-compat-add-clock_nanosleep-support.patch
deleted file mode 100644
index f6327a921beedda67979ee4775eab506464a81a7..0000000000000000000000000000000000000000
--- a/vlc_patches/0009-compat-add-clock_nanosleep-support.patch
+++ /dev/null
@@ -1,126 +0,0 @@
-From 39c77bd0970d509bd47df2d0c1e58b09281067e2 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 5 Sep 2019 23:54:30 +0200
-Subject: [PATCH 09/15] compat: add clock_nanosleep support
-
-clock_nanosleep() is not supported in emscripten
-
-Implementation from : https://code.woboq.org/userspace/glibc/sysdeps/unix/clock_nanosleep.c.html
----
- compat/clock_nanosleep.c | 91 ++++++++++++++++++++++++++++++++++++++++
- configure.ac             |  1 +
- 2 files changed, 92 insertions(+)
- create mode 100644 compat/clock_nanosleep.c
-
-diff --git a/compat/clock_nanosleep.c b/compat/clock_nanosleep.c
-new file mode 100644
-index 0000000000..d4cfee70e6
---- /dev/null
-+++ b/compat/clock_nanosleep.c
-@@ -0,0 +1,91 @@
-+/*****************************************************************************
-+ * clock_nanosleep.c: 
-+         High-resolution sleep with the specified clock for emscripten.
-+ *****************************************************************************
-+ * Copyright © 2019 VLC authors, VideoLAN
-+ * and Free Software Foundation, Inc.
-+ * 
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU Lesser General Public License as published by
-+ * the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program; if not, write to the Free Software Foundation,
-+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
-+ *****************************************************************************/
-+
-+#ifdef HAVE_CONFIG_H
-+# include "config.h"
-+#endif
-+
-+#include <assert.h>
-+#include <errno.h>
-+#include <time.h>
-+// #include <sysdep-cancel.h>
-+
-+/* This implementation assumes that these is only a `nanosleep' system
-+   call.  So we have to remap all other activities.  */
-+int
-+//__clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
-+//                  struct timespec *rem)
-+clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
-+                   struct timespec *rem)
-+{
-+  struct timespec now;
-+
-+  if (__builtin_expect (req->tv_nsec, 0) < 0
-+      || __builtin_expect (req->tv_nsec, 0) >= 1000000000)
-+    return EINVAL;
-+
-+  if (clock_id == CLOCK_THREAD_CPUTIME_ID)
-+    return EINVAL;                /* POSIX specifies EINVAL for this case.  */
-+
-+  if (clock_id < CLOCK_REALTIME || clock_id > CLOCK_THREAD_CPUTIME_ID)
-+    return EINVAL;
-+
-+  /* If we got an absolute time, remap it.  */
-+  if (flags == TIMER_ABSTIME)
-+    {
-+      long int nsec;
-+      long int sec;
-+
-+      /* Make sure we use safe data types.  */
-+      assert (sizeof (sec) >= sizeof (now.tv_sec));
-+
-+      /* Get the current time for this clock.  */
-+      //if (__clock_gettime (clock_id, &now) != 0)
-+      if (clock_gettime (clock_id, &now) != 0)
-+        return errno;
-+
-+      /* Compute the difference.  */
-+      nsec = req->tv_nsec - now.tv_nsec;
-+      sec = req->tv_sec - now.tv_sec - (nsec < 0);
-+      if (sec < 0)
-+        /* The time has already elapsed.  */
-+        return 0;
-+
-+      now.tv_sec = sec;
-+      now.tv_nsec = nsec + (nsec < 0 ? 1000000000 : 0);
-+
-+      /* From now on this is our time.  */
-+      req = &now;
-+
-+      /* Make sure we are not modifying the struct pointed to by REM.  */
-+      rem = NULL;
-+    }
-+  else if (flags != 0)
-+    return EINVAL;
-+
-+  else if (clock_id != CLOCK_REALTIME)
-+    /* Not supported.  */
-+    return ENOTSUP;
-+  //  return __nanosleep (req, rem), 0 ? errno : 0;
-+  return nanosleep (req, rem), 0 ? errno : 0;
-+}
-+//weak_alias (__clock_nanosleep, clock_nanosleep)
-diff --git a/configure.ac b/configure.ac
-index e5298e6f7a..5f24ab5945 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -325,6 +325,7 @@ case "${host_os}" in
-   *emscripten*)
-     SYS=emscripten
-     AC_LIBOBJ([sigwait])
-+    AC_LIBOBJ([clock_nanosleep])
-     CC=emcc
-     LD=emcc
-     LDSHARED=emcc
--- 
-2.23.0
-
diff --git a/vlc_patches/0010-emscripten-add-vlc_getProxyUrl-stub.patch b/vlc_patches/0010-emscripten-add-vlc_getProxyUrl-stub.patch
deleted file mode 100644
index 7cab9d4982caea46a7e3086b9cf03e3e57ff5512..0000000000000000000000000000000000000000
--- a/vlc_patches/0010-emscripten-add-vlc_getProxyUrl-stub.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From 1f14176411e65c0d12fe167e62a8f7f10f233216 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Mon, 24 Feb 2020 14:15:56 +0100
-Subject: [PATCH 1/1] emscripten: add vlc_getProxyUrl stub.
-
----
- src/Makefile.am          |  3 ++-
- src/emscripten/netconf.c | 39 +++++++++++++++++++++++++++++++++++++++
- 2 files changed, 41 insertions(+), 1 deletion(-)
- create mode 100644 src/emscripten/netconf.c
-
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 474ddcdd63..64d28e12c4 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -445,7 +445,8 @@ libvlccore_la_SOURCES += \
- 	posix/dirs.c \
- 	posix/filesystem.c \
- 	posix/specific.c \
--	posix/timer.c
-+	posix/timer.c \
-+	emscripten/netconf.c
- endif
- 
- if HAVE_DARWIN
-diff --git a/src/emscripten/netconf.c b/src/emscripten/netconf.c
-new file mode 100644
-index 0000000000..b972bf4976
---- /dev/null
-+++ b/src/emscripten/netconf.c
-@@ -0,0 +1,39 @@
-+/*****************************************************************************
-+ * netconf.c : Network configuration
-+ *****************************************************************************
-+ * Copyright (C) 2019
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU Lesser General Public License as published by
-+ * the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program; if not, write to the Free Software Foundation,
-+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
-+ *****************************************************************************/
-+
-+#ifdef HAVE_CONFIG_H
-+# include "config.h"
-+#endif
-+
-+#include <string.h>
-+
-+char *vlc_getProxyUrl(const char *url);
-+
-+/**
-+ * Determines the network proxy server to use (if any).
-+ * @param url absolute URL for which to get the proxy server
-+ * @return proxy URL, NULL if no proxy or error
-+ */
-+char *vlc_getProxyUrl(const char *url)
-+{
-+  char *proxy = strdup(url);
-+  
-+  return proxy;
-+}
--- 
-2.25.0
-
diff --git a/vlc_patches/0011-configure-disable-deprecated-GL-functions-for-emscri.patch b/vlc_patches/0011-configure-disable-deprecated-GL-functions-for-emscri.patch
deleted file mode 100644
index c96f64eb142098f9cc8d6d642984c1d483eaf20f..0000000000000000000000000000000000000000
--- a/vlc_patches/0011-configure-disable-deprecated-GL-functions-for-emscri.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From a2a2372eb588474073873cd745164e22ef076328 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Mon, 9 Sep 2019 19:07:49 +0200
-Subject: [PATCH 11/15] configure: disable deprecated GL functions for
- emscripten
-
-The build system assumes OpenGL functions are implemented if the headers are defined.
-Which is wrong, so we need to disable the HAVE_GL marker.
----
- configure.ac | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/configure.ac b/configure.ac
-index 5f24ab5945..66a69a91d7 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -3231,6 +3231,7 @@ PKG_CHECK_MODULES([GL], [gl], [
-   ])
-   AC_MSG_RESULT([${have_gl}])
- ])
-+AS_IF([test "${SYS}" = "emscripten"], [have_gl="no"], [have_gl="yes"])
- AM_CONDITIONAL([HAVE_GL], [test "${have_gl}" = "yes"])
- AS_IF([test "${have_gl}" = "yes"], [
-   AC_DEFINE([HAVE_GL], 1, [Defined if having OpenGL])
--- 
-2.23.0
-
diff --git a/vlc_patches/0014-vout-add-emscripten-gl-es2-and-window-modules.patch b/vlc_patches/0014-vout-add-emscripten-gl-es2-and-window-modules.patch
deleted file mode 100644
index fff95767123bcadc4273d196c64e767d0832acc6..0000000000000000000000000000000000000000
--- a/vlc_patches/0014-vout-add-emscripten-gl-es2-and-window-modules.patch
+++ /dev/null
@@ -1,222 +0,0 @@
-From 94cae7fd97cbdfe003d73aab433a031032556a50 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 12 Sep 2019 15:03:28 +0200
-Subject: [PATCH 14/15] vout: add emscripten gl es2 and window modules
-
-The module contains a window module that sets
-the window type and a gl es2 submodule implementing
-webgl function calls.
-
-Co-authored-by: Etienne Brateau <etienne@videolabs.io>
----
- modules/video_output/Makefile.am  |   9 ++
- modules/video_output/emscripten.c | 177 ++++++++++++++++++++++++++++++
- 2 files changed, 186 insertions(+)
- create mode 100644 modules/video_output/emscripten.c
-
-diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
-index a5b59f911a..f737781633 100644
---- a/modules/video_output/Makefile.am
-+++ b/modules/video_output/Makefile.am
-@@ -469,6 +469,15 @@ libcaca_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
- EXTRA_LTLIBRARIES += libcaca_plugin.la
- vout_LTLIBRARIES += $(LTLIBcaca)
- 
-+### Emscripten ###
-+libemscripten_window_plugin_la_SOURCES = video_output/emscripten.c
-+libemscripten_window_plugin_la_CFLAGS = $(AM_CFLAGS)
-+libemscripten_window_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
-+
-+if HAVE_EMSCRIPTEN
-+vout_LTLIBRARIES += libemscripten_window_plugin.la
-+endif
-+
- ### Common ###
- 
- libflaschen_plugin_la_SOURCES = video_output/flaschen.c
-diff --git a/modules/video_output/emscripten.c b/modules/video_output/emscripten.c
-new file mode 100644
-index 0000000000..1e582980e0
---- /dev/null
-+++ b/modules/video_output/emscripten.c
-@@ -0,0 +1,177 @@
-+/**
-+ * @file emscripten.c
-+ * @brief Emscripten webgl video output for VLC media player
-+ */
-+/*****************************************************************************
-+ * Copyright © 2019 VLC authors and VideoLAN
-+ *
-+ * Authors:   Etienne Brateau <etienne@videolabs.io>
-+ *            Mehdi Sabwat <mehdisabwat@gmail.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU Lesser General Public License as published by
-+ * the Free Software Foundation; either version 2.1 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public License
-+ * along with this program; if not, write to the Free Software Foundation,
-+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
-+ *****************************************************************************/
-+
-+#ifdef HAVE_CONFIG_H
-+# include <config.h>
-+#endif
-+
-+#include <stdarg.h>
-+
-+#include <vlc_common.h>
-+#include <vlc_plugin.h>
-+#include <vlc_vout_window.h>
-+#include <vlc_vout_display.h>
-+#include <vlc_opengl.h>
-+
-+#include "./opengl/vout_helper.h"
-+
-+#include <emscripten.h>
-+#include <emscripten/html5.h>
-+
-+static const struct vout_window_operations ops = {
-+       //TODO: Implement canvas operations
-+       //vout_window_ReportSize() should be called from here
-+};
-+
-+typedef struct gl_sys_t
-+{
-+  unsigned width;
-+  unsigned height;
-+
-+  EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
-+} gl_sys_t;
-+
-+static int OpenWindow(vout_window_t *wnd)
-+{
-+    wnd->type = VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL;
-+    wnd->ops = &ops;
-+
-+    return VLC_SUCCESS;
-+}
-+
-+static void *GetProcAddress(vlc_gl_t *gl, const char *name)
-+{
-+  // Not needed for OpenGL ES2
-+  VLC_UNUSED(gl);
-+  VLC_UNUSED(name);
-+  return NULL;
-+}
-+static int MakeCurrent(vlc_gl_t *gl)
-+{
-+  gl_sys_t *sys = gl->sys;
-+
-+  if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS)
-+    return VLC_EGENERIC;
-+  return VLC_SUCCESS;
-+}
-+
-+static void ReleaseCurrent(vlc_gl_t *gl)
-+{
-+  VLC_UNUSED(gl);
-+  emscripten_webgl_make_context_current(0);
-+}
-+
-+static void Swap(vlc_gl_t *gl)
-+{
-+  VLC_UNUSED(gl);
-+  emscripten_webgl_commit_frame();
-+}
-+
-+static void Resize(vlc_gl_t *gl, unsigned w, unsigned h)
-+{
-+  // to implement
-+  VLC_UNUSED(gl);
-+  VLC_UNUSED(w);
-+  VLC_UNUSED(h);
-+}
-+
-+static void Close (vlc_gl_t *gl)
-+{
-+  free(gl->sys);
-+}
-+
-+static int Open (vlc_gl_t *gl, unsigned width, unsigned height)
-+{  
-+  VLC_UNUSED(width), VLC_UNUSED(height); // to implement
-+
-+  EmscriptenWebGLContextAttributes attr;
-+
-+  emscripten_webgl_init_context_attributes(&attr);
-+  attr.explicitSwapControl = 1;
-+    
-+  vout_window_t *wnd = gl->surface;
-+  
-+  if (wnd->type != VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL)
-+    goto error;
-+
-+  gl_sys_t *sys;
-+  
-+  gl->sys = sys = calloc(1, sizeof(*sys));
-+  if (!sys)
-+    return VLC_EGENERIC;
-+  
-+  sys->context = emscripten_webgl_create_context("#canvas", &attr);  
-+  if (!sys->context)
-+  {
-+    msg_Err(gl, "Failed to make context current");
-+    goto error;
-+  }
-+
-+  // Check that the WebGL context is valid
-+  if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS)
-+  {
-+    emscripten_log(EM_LOG_CONSOLE, "failed to make context current");
-+    goto error;
-+  }
-+
-+  // Release the context
-+  emscripten_webgl_make_context_current(0);
-+  wnd->handle.em_context = sys->context;
-+
-+  // Implement egl routines: 
-+  gl->makeCurrent = MakeCurrent;
-+  gl->releaseCurrent = ReleaseCurrent;
-+  gl->resize = Resize;
-+  gl->swap = Swap;
-+  gl->getProcAddress = GetProcAddress;
-+  gl->destroy = Close;
-+
-+  return VLC_SUCCESS;
-+error:
-+  Close(gl);
-+  return VLC_EGENERIC;
-+}
-+
-+/*
-+ * Module descriptor
-+ */
-+vlc_module_begin()
-+    set_shortname(N_("Emscripten Window"))
-+    set_description(N_("Emscripten drawing area"))
-+    set_category(CAT_VIDEO)
-+    set_subcategory(SUBCAT_VIDEO_VOUT)
-+    set_capability("vout window", 10)
-+    set_callbacks(OpenWindow, NULL)
-+
-+    add_submodule ()
-+    set_shortname("Emscripten GL")
-+    set_description(N_("Emscripten extension for OpenGL"))
-+    set_category(CAT_VIDEO)
-+    set_subcategory(SUBCAT_VIDEO_VOUT)
-+    set_capability("opengl es2", 50)
-+    set_callback(Open)
-+    add_shortcut("emscripten-gl", "gles2")
-+vlc_module_end()
-+
--- 
-2.23.0
-
diff --git a/vlc_patches/0015-vlc_common-add-weak-attribute-support-for-wasm.patch b/vlc_patches/0015-vlc_common-add-weak-attribute-support-for-wasm.patch
deleted file mode 100644
index 319a2a565a100feb391f1adab0dfa5c7d25cc02d..0000000000000000000000000000000000000000
--- a/vlc_patches/0015-vlc_common-add-weak-attribute-support-for-wasm.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 7b4679fbdf714f8b6c3ea79a650d1bcdb1acd240 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Wed, 25 Sep 2019 20:20:59 +0200
-Subject: [PATCH 15/15] vlc_common: add weak attribute support for wasm
-
----
- include/vlc_common.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/vlc_common.h b/include/vlc_common.h
-index e4c5cd85c9..359c53dac8 100644
---- a/include/vlc_common.h
-+++ b/include/vlc_common.h
-@@ -185,7 +185,7 @@
- # define VLC_USED
- #endif
- 
--#if defined (__ELF__) || defined (__MACH__)
-+#if defined (__ELF__) || defined (__MACH__) || defined (__wasm__)
- # define VLC_WEAK __attribute__((weak))
- #else
- /**
--- 
-2.23.0
-
diff --git a/vlc_patches/0016-Add-meson_system_name-for-emscripten.patch b/vlc_patches/0016-Add-meson_system_name-for-emscripten.patch
deleted file mode 100644
index 43cb8c1a353673761ff0e18a938613fca1d10e77..0000000000000000000000000000000000000000
--- a/vlc_patches/0016-Add-meson_system_name-for-emscripten.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 013429a1e49a2a11a9875400ca99ca8163eb219c Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Tue, 7 Jan 2020 11:57:22 +0100
-Subject: [PATCH 1/2] Add meson_system_name for emscripten
-
----
- contrib/src/main.mak | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/contrib/src/main.mak b/contrib/src/main.mak
-index a0469fb3e0..15be3d5638 100644
---- a/contrib/src/main.mak
-+++ b/contrib/src/main.mak
-@@ -658,12 +658,16 @@ else
- ifdef HAVE_LINUX
- 	# android has also system = linux and defines HAVE_LINUX
- 	MESON_SYSTEM_NAME = linux
-+else
-+ifdef HAVE_EMSCRIPTEN
-+        MESON_SYSTEM_NAME = emscripten
- else
- 	$(error "No meson system name known for this target")
- endif
- endif
- endif
- endif
-+endif
- 
- crossfile.meson: $(SRC)/gen-meson-crossfile.py
- 	$(HOSTVARS_MESON) \
--- 
-2.24.1
-
diff --git a/vlc_patches/audio_output/0000-aout-add-emscripten-audio-worklet-module.patch b/vlc_patches/audio_output/0000-aout-add-emscripten-audio-worklet-module.patch
new file mode 100644
index 0000000000000000000000000000000000000000..47292530b43c0a216c133e53a9c018192279f003
--- /dev/null
+++ b/vlc_patches/audio_output/0000-aout-add-emscripten-audio-worklet-module.patch
@@ -0,0 +1,438 @@
+From cb674e3724db5e204a60277e69c85765f8ad2141 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Fri, 30 Apr 2021 01:15:17 +0200
+Subject: [PATCH] aout: add emscripten audio worklet module
+
+---
+ modules/audio_output/Makefile.am    |   5 +
+ modules/audio_output/emscripten.cpp | 405 ++++++++++++++++++++++++++++
+ 2 files changed, 410 insertions(+)
+ create mode 100644 modules/audio_output/emscripten.cpp
+
+diff --git a/modules/audio_output/Makefile.am b/modules/audio_output/Makefile.am
+index 194c421e54..7c87faaea8 100644
+--- a/modules/audio_output/Makefile.am
++++ b/modules/audio_output/Makefile.am
+@@ -117,3 +117,8 @@ endif
+ if HAVE_TVOS
+ aout_LTLIBRARIES += libaudiounit_ios_plugin.la
+ endif
++
++libemworklet_audio_plugin_la_SOURCES = audio_output/emscripten.cpp
++if HAVE_EMSCRIPTEN
++aout_LTLIBRARIES += libemworklet_audio_plugin.la
++endif
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+new file mode 100644
+index 0000000000..1c890b3f6d
+--- /dev/null
++++ b/modules/audio_output/emscripten.cpp
+@@ -0,0 +1,405 @@
++/*****************************************************************************
++ * emscripten.c: audio output module using audio worklets
++ *****************************************************************************
++ * Copyright © 2020 VLC authors and VideoLAN
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ *****************************************************************************/
++
++#ifdef HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <assert.h>
++#include <vlc_common.h>
++#include <vlc_plugin.h>
++#include <vlc_aout.h>
++
++#include <emscripten.h>
++#include <emscripten/val.h>
++#include <emscripten/bind.h>
++#include <emscripten/html5.h>
++
++#include <cstdint>
++#include <stdlib.h>
++
++#define STORAGE_SIZE 1024 * 1024
++// Sample rate might change, and it would be good to be able to change it during playback.
++#define AUDIO_WORKLET_SAMPLE_RATE 44100
++// Don't know any way to get the browser's supported number of channels.
++#define AUDIO_WORKLET_NB_CHANNELS 2
++
++using namespace emscripten;
++namespace {	   
++	EM_BOOL requestAnimationFrame_cb( double time, void *userData );
++
++	class AWNodeWrapper {
++	public:
++		val context = val::undefined();
++		val getCtx() const { return context; };
++		void setCtx(val v_context) { context = v_context; };
++		
++		uintptr_t sab_ptr;
++		uintptr_t getSabPtr() const { return sab_ptr; };
++		void setSabPtr(uintptr_t p_sab) { sab_ptr = p_sab; };
++		
++		size_t sab_size;
++		size_t getSabSize() const { return sab_size; };
++		void setSabSize(size_t s_size) { sab_size = s_size; };
++		
++		int8_t channels;
++		int8_t getChannels() const { return channels; };
++		void setChannels(int8_t chan) { channels = chan; };
++		
++		AWNodeWrapper(int sample_rate) {
++			// Prepare audio context options
++			val audio_ctx_options = val::object();
++			audio_ctx_options.set("sampleRate", sample_rate);
++			
++			context = val::global("AudioContext").new_(audio_ctx_options);
++			context.call<void>("suspend");
++		}
++		
++		val operator()( val undefined_promise_argument ) {
++			(val)undefined_promise_argument;
++			
++			// Prepare AWN Options
++			val awn_options = val::object();
++			val awn_opt_outputChannelCount = val::array();
++			awn_opt_outputChannelCount.call<val>("push", channels);
++			awn_options.set("outputChannelCount", awn_opt_outputChannelCount);
++			awn_options.set("numberOfInputs", 0);
++			awn_options.set("numberOfOutputs", 1);
++			
++			val AudioNode = val::global("AudioWorkletNode").new_(context, std::string("worklet-processor"), awn_options);
++			AudioNode.set("channelCount", channels);
++			
++			//Prepare postMessage message
++			val msg = val::object();
++			msg.set("type", std::string("recv-audio-queue"));
++			msg.set("data", val::module_property("wasmMemory")["buffer"]);
++			msg.set("sab_ptr", sab_ptr);
++			msg.set("sab_size", sab_size);
++			
++			AudioNode["port"].call<val>("postMessage", msg);
++			AudioNode.call<val>("connect", context["destination"]);
++			
++			emscripten_request_animation_frame_loop(requestAnimationFrame_cb, this);
++			
++			return val::undefined();
++		}
++	};
++	
++	EMSCRIPTEN_BINDINGS(AWWSCOPE) {
++		class_<AWNodeWrapper>("awn_cb_wrapper")
++			.constructor<int>()
++			.property("context", &AWNodeWrapper::getCtx, &AWNodeWrapper::setCtx)
++			.property("sab_ptr", &AWNodeWrapper::getSabPtr, &AWNodeWrapper::setSabPtr)
++			.property("sab_size", &AWNodeWrapper::getSabSize, &AWNodeWrapper::setSabSize)
++			.property("channels", &AWNodeWrapper::getChannels, &AWNodeWrapper::setChannels)
++			.function("awn_call", &AWNodeWrapper::operator());
++	};
++	
++	typedef struct aout_sys_t
++	{
++		int8_t *sab;
++		size_t sab_size;
++		AWNodeWrapper *awn_inst;
++		float volume;
++		
++	} aout_sys_t;
++
++	EM_BOOL requestAnimationFrame_cb( double time, void *userData ) {
++		(double) time;
++		AWNodeWrapper *inst = reinterpret_cast<AWNodeWrapper *>(userData);
++		uint32_t *sab = reinterpret_cast<uint32_t *>(inst->getSabPtr());
++		val view = val(typed_memory_view(inst->getSabSize(), sab));
++		val context = inst->getCtx();
++		if ( view[0].as<int>() == 1 ) {
++			context.call<val>("resume");
++			sab[0] = 0;
++			return EM_FALSE;
++		}
++		return EM_TRUE;
++	}
++	
++	// For Atomics.store() and .load() only integer types are supported
++	unsigned int js_index_load(int8_t *sab_ptr, int8_t index, size_t sab_size){
++		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr); 
++		val buffer = val(typed_memory_view(sab_size, buffer_view));
++		
++		return val::global("Atomics").call<unsigned int>("load", buffer, index);
++	}
++	
++	void js_index_store(int8_t *sab_ptr, int8_t index, unsigned int value, size_t sab_size) {
++		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr); 
++		val buffer = val(typed_memory_view(sab_size, buffer_view));
++		
++		return val::global("Atomics").call<void>("store", buffer, index, value);
++	}
++
++	// careful when calling this, you cannot wait on any index
++	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
++	unsigned int js_index_wait(int8_t *sab_ptr, int8_t index, size_t sab_size) {
++		int32_t *buffer_view = reinterpret_cast<int32_t *>(sab_ptr); 
++		val buffer = val(typed_memory_view(sab_size, buffer_view));
++
++		return val::global("Atomics").call<unsigned int>("wait", buffer, index);
++	}
++
++	void Flush( audio_output_t *aout )
++	{
++		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
++		bzero(sys->sab, sys->sab_size);
++	}
++	
++	int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
++	{
++		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
++		unsigned nbChannels = aout_FormatNbChannels(fmt);
++
++		if (( nbChannels == 0 ) || !AOUT_FMT_LINEAR(fmt))
++			return VLC_EGENERIC;
++		fmt->i_format = VLC_CODEC_FL32;
++		fmt->i_channels = AUDIO_WORKLET_NB_CHANNELS;
++		fmt->i_rate = AUDIO_WORKLET_SAMPLE_RATE;
++
++		// resume audio context (first start, it is paused when initialized)
++		js_index_store(sys->sab, 4, (int)sys->volume * 100, sys->sab_size);
++		js_index_store(sys->sab, 0, 1, sys->sab_size);
++
++		return VLC_SUCCESS;
++	}
++
++	void Stop (audio_output_t *aout)
++	{
++		Flush(aout);
++	}
++	
++	int audio_worklet_push (audio_output_t *aout, const int8_t *data, unsigned data_size) {
++		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
++		int8_t *sab_view = sys->sab + 5 * sizeof(int32_t);
++		unsigned head = js_index_load(sys->sab, 1, sys->sab_size);
++
++		// TODO: check that we do not write on unconsumed data.
++		if (head + data_size > STORAGE_SIZE) {
++			// Copy the part of the data at the buffer end
++			unsigned data_size_copy_end = STORAGE_SIZE - head;
++			memcpy(sab_view + head, data, data_size_copy_end);
++			head = 0;
++			
++			// Copy the part of the data at the buffer start
++			unsigned data_size_copy_start = data_size - data_size_copy_end;
++			memcpy(sab_view + head, data, data_size_copy_start);
++			head = data_size_copy_start;
++		}
++		else {
++			memcpy(sab_view + head, data, data_size);
++			head += data_size;
++		}
++		js_index_store(sys->sab, 1, head, sys->sab_size);
++		return 0;  // return success to indicate successful push.
++	}
++
++	void Play( audio_output_t *aout, block_t *block, vlc_tick_t date)
++	{
++		VLC_UNUSED(date);
++		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
++		const int8_t* data = (int8_t *)block->p_buffer;
++		size_t data_size = block->i_buffer;
++		
++		unsigned head = js_index_load(sys->sab, 1, sys->sab_size);
++		unsigned tail = js_index_load(sys->sab, 2, sys->sab_size);
++		unsigned new_head = (head + data_size) % STORAGE_SIZE;
++		if (new_head > tail)
++		{
++			// the worklet processor keeps rendering  until tail matches head
++			// it will be notified by an Atomics.notify() from the process() callback
++			js_index_wait(sys->sab, 3, sys->sab_size);
++		}
++		audio_worklet_push(aout, data, data_size);
++		block_Release(block);
++	}
++	
++	void Pause( audio_output_t *aout, bool paused, vlc_tick_t date )
++	{
++		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
++		if (paused == false) {
++			js_index_store(sys->sab, 0, 0, sys->sab_size);
++		}
++		else {
++			js_index_store(sys->sab, 0, 1, sys->sab_size);
++		}
++		VLC_UNUSED(date);
++		Flush(aout);
++	}
++	
++	int Time_Get( audio_output_t *aout, vlc_tick_t *delay)
++	{
++		return aout_TimeGetDefault(aout, delay);
++	}
++
++	void Close( vlc_object_t *obj )
++	{
++		audio_output_t *aout = (audio_output_t *)obj;
++		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
++
++		delete sys->awn_inst;
++		free(sys->sab);
++		free(sys);
++	}
++
++	int Volume_Set( audio_output_t *aout, float volume)
++	{
++		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
++
++		if (volume > 1.0f)
++			volume = 1.0f;
++		else if (volume < 0.0f)
++			volume = 0.0f;
++		// TODO: implement gain
++		sys->volume = volume;
++		js_index_store(sys->sab, 4, (int)volume * 100, sys->sab_size);
++		aout_VolumeReport(aout, volume);
++
++		return 0;
++	}
++
++	int Mute_Set( audio_output_t *aout, bool mute)
++	{
++		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
++
++		if (mute == 0)
++			js_index_store(sys->sab, 4, 0, sys->sab_size);
++		else
++			js_index_store(sys->sab, 4, (int)sys->volume * 100, sys->sab_size);
++		aout_MuteReport(aout, mute);
++		return 0;
++	}
++
++	
++	int Open( vlc_object_t *obj )
++	{
++		audio_output_t * aout = (audio_output_t *) obj;
++		
++		/* Allocate structures */
++		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(malloc( sizeof( *sys ) ));
++		if( unlikely(sys == NULL) )
++			return VLC_ENOMEM;
++		
++		aout->sys = sys;
++		aout->start = Start;
++		aout->stop = Stop;
++		aout->play = Play;
++		aout->pause = Pause;
++		aout->flush = Flush;
++		aout->time_get = Time_Get;
++		aout->volume_set = Volume_Set;
++		aout->mute_set = Mute_Set;
++		
++		sys->awn_inst = new AWNodeWrapper(AUDIO_WORKLET_SAMPLE_RATE);
++		sys->sab_size = 5 * sizeof(int32_t) + STORAGE_SIZE;
++		sys->sab = reinterpret_cast<int8_t *>(malloc( sys->sab_size ));
++		sys->volume = 1.0f;
++
++		if ( unlikely(sys->sab == NULL) )
++			return VLC_ENOMEM;
++		bzero(sys->sab, sys->sab_size);
++		
++		val webaudio_context = sys->awn_inst->getCtx();
++		
++		// Prepare audioWorkletProcessor blob
++		val document = val::global("document");
++		val script = document.call<val>("createElement", std::string("SCRIPT"));
++		script.set("type", std::string("worklet"));
++		std::string processorStr = "class Processor extends AudioWorkletProcessor { \
++	constructor() { \
++		super(); \
++		this.port.onmessage = e => { \
++			if (e.data.type === 'recv-audio-queue') { \
++				this.buf = e.data.data; \
++				this.capacity = e.data.sab_size / 4; \
++				this.flag = new Uint32Array(this.buf, e.data.sab_ptr, 1); \
++				this.head = new Uint32Array(this.buf, e.data.sab_ptr + 4, 1); \
++				this.tail = new Uint32Array(this.buf, e.data.sab_ptr + 8, 1); \
++				this.can_write = new Int32Array(this.buf, e.data.sab_ptr + 12, 1); \
++				this.volume = new Int32Array(this.buf, e.data.sab_ptr + 16, 1); \
++				this.storage = new Float32Array(this.buf, e.data.sab_ptr + 20, this.capacity); \
++			} else { \
++				throw 'unexpected.'; \
++			} \
++		}; \
++	} \
++	process(inputs, outputs, parameters) { \
++		const output = outputs[0]; \
++		const nbChannels = output.length; \
++		const nbSamples = output[0].length; \
++		if (this.head.buffer.byteLength == 0) { \
++			throw new Error('wasmMemory grew'); \
++		} \
++		var head = Atomics.load(this.head, 0) / 4; \
++		var tail = Atomics.load(this.tail, 0) / 4; \
++		var i = 0; \
++		var volume = (Atomics.load(this.volume, 0) / 4) / 100; \
++		while (tail != head && i < nbSamples) \
++		{ \
++			for (let c = 0; c < nbChannels; ++c) { \
++				output[c][i] = this.storage[tail] * volume; \
++				tail++; \
++				if (tail == this.capacity) { \
++					tail = 0; \
++				} \
++			} \
++			i++; \
++		} \
++		Atomics.store(this.tail, 0, tail * 4); \
++		Atomics.notify(this.can_write, 0); \
++		return true; \
++	} \
++} \
++registerProcessor('worklet-processor', Processor);";
++		script.set("innerText", processorStr);
++		val ProcessorTextArray = val::array();
++		ProcessorTextArray.call<val>("push", script["innerText"]);
++		val BlobObject = val::object();
++		BlobObject.set("type", std::string("application/javascript"));
++		val WorkletModuleUrl = val::global("URL").call<val>("createObjectURL", val::global("Blob").new_(ProcessorTextArray, BlobObject));
++		
++		// Prepare audioWorkletProcessor callback
++		val cb_caller = val::module_property("awn_cb_wrapper").new_(AUDIO_WORKLET_SAMPLE_RATE);
++		cb_caller.set("context", val(webaudio_context));
++		cb_caller.set("sab_ptr", val(reinterpret_cast<uintptr_t>(sys->sab)));
++		cb_caller.set("sab_size", val(sys->sab_size));
++		cb_caller.set("channels", val(AUDIO_WORKLET_NB_CHANNELS));
++		val awn_caller = cb_caller["awn_call"];
++		val awn_cb = awn_caller.call<val>("bind", cb_caller);
++		
++		// start audio worklet (since the context is suspended, sound won't start now
++		// Since the WebAudio Context cannot be created in a worker, we create
++		// it in the main_thread and use the SAB to signal it when we want it to start
++		webaudio_context["audioWorklet"].call<val>("addModule", WorkletModuleUrl).call<val>("then", awn_cb);
++
++		return VLC_SUCCESS;
++	}
++}
++
++vlc_module_begin ()
++	set_description( N_("Emscripten Worklet audio output") )
++	set_shortname( "emworklet" )
++	set_capability( "audio output", 100 )
++	set_category( CAT_AUDIO )
++	set_subcategory( SUBCAT_AUDIO_AOUT )
++	set_callbacks( Open, Close )
++vlc_module_end ()
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0001-Normalize-formatting-in-audio_output-emscripten.cpp.patch b/vlc_patches/audio_output/0001-Normalize-formatting-in-audio_output-emscripten.cpp.patch
new file mode 100644
index 0000000000000000000000000000000000000000..06b5a820595a07e554b07413ec1c54827dc1c947
--- /dev/null
+++ b/vlc_patches/audio_output/0001-Normalize-formatting-in-audio_output-emscripten.cpp.patch
@@ -0,0 +1,261 @@
+From 03699f0b49461b93b588cbb01b42ff1ded93c5ed Mon Sep 17 00:00:00 2001
+From: Olivier FAURE <couteaubleu@gmail.com>
+Date: Sun, 30 May 2021 12:01:49 +0200
+Subject: [PATCH 1/5] Normalize formatting in audio_output/emscripten.cpp
+
+---
+ modules/audio_output/emscripten.cpp | 74 ++++++++++++++---------------
+ 1 file changed, 37 insertions(+), 37 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index 1c890b3f6d..b704b90699 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -42,7 +42,7 @@
+ #define AUDIO_WORKLET_NB_CHANNELS 2
+ 
+ using namespace emscripten;
+-namespace {	   
++namespace {
+ 	EM_BOOL requestAnimationFrame_cb( double time, void *userData );
+ 
+ 	class AWNodeWrapper {
+@@ -50,31 +50,31 @@ namespace {
+ 		val context = val::undefined();
+ 		val getCtx() const { return context; };
+ 		void setCtx(val v_context) { context = v_context; };
+-		
++
+ 		uintptr_t sab_ptr;
+ 		uintptr_t getSabPtr() const { return sab_ptr; };
+ 		void setSabPtr(uintptr_t p_sab) { sab_ptr = p_sab; };
+-		
++
+ 		size_t sab_size;
+ 		size_t getSabSize() const { return sab_size; };
+ 		void setSabSize(size_t s_size) { sab_size = s_size; };
+-		
++
+ 		int8_t channels;
+ 		int8_t getChannels() const { return channels; };
+ 		void setChannels(int8_t chan) { channels = chan; };
+-		
++
+ 		AWNodeWrapper(int sample_rate) {
+ 			// Prepare audio context options
+ 			val audio_ctx_options = val::object();
+ 			audio_ctx_options.set("sampleRate", sample_rate);
+-			
++
+ 			context = val::global("AudioContext").new_(audio_ctx_options);
+ 			context.call<void>("suspend");
+ 		}
+-		
++
+ 		val operator()( val undefined_promise_argument ) {
+ 			(val)undefined_promise_argument;
+-			
++
+ 			// Prepare AWN Options
+ 			val awn_options = val::object();
+ 			val awn_opt_outputChannelCount = val::array();
+@@ -82,26 +82,26 @@ namespace {
+ 			awn_options.set("outputChannelCount", awn_opt_outputChannelCount);
+ 			awn_options.set("numberOfInputs", 0);
+ 			awn_options.set("numberOfOutputs", 1);
+-			
++
+ 			val AudioNode = val::global("AudioWorkletNode").new_(context, std::string("worklet-processor"), awn_options);
+ 			AudioNode.set("channelCount", channels);
+-			
++
+ 			//Prepare postMessage message
+ 			val msg = val::object();
+ 			msg.set("type", std::string("recv-audio-queue"));
+ 			msg.set("data", val::module_property("wasmMemory")["buffer"]);
+ 			msg.set("sab_ptr", sab_ptr);
+ 			msg.set("sab_size", sab_size);
+-			
++
+ 			AudioNode["port"].call<val>("postMessage", msg);
+ 			AudioNode.call<val>("connect", context["destination"]);
+-			
++
+ 			emscripten_request_animation_frame_loop(requestAnimationFrame_cb, this);
+-			
++
+ 			return val::undefined();
+ 		}
+ 	};
+-	
++
+ 	EMSCRIPTEN_BINDINGS(AWWSCOPE) {
+ 		class_<AWNodeWrapper>("awn_cb_wrapper")
+ 			.constructor<int>()
+@@ -111,14 +111,14 @@ namespace {
+ 			.property("channels", &AWNodeWrapper::getChannels, &AWNodeWrapper::setChannels)
+ 			.function("awn_call", &AWNodeWrapper::operator());
+ 	};
+-	
++
+ 	typedef struct aout_sys_t
+ 	{
+ 		int8_t *sab;
+ 		size_t sab_size;
+ 		AWNodeWrapper *awn_inst;
+ 		float volume;
+-		
++
+ 	} aout_sys_t;
+ 
+ 	EM_BOOL requestAnimationFrame_cb( double time, void *userData ) {
+@@ -134,26 +134,26 @@ namespace {
+ 		}
+ 		return EM_TRUE;
+ 	}
+-	
++
+ 	// For Atomics.store() and .load() only integer types are supported
+ 	unsigned int js_index_load(int8_t *sab_ptr, int8_t index, size_t sab_size){
+-		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr); 
++		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr);
+ 		val buffer = val(typed_memory_view(sab_size, buffer_view));
+-		
++
+ 		return val::global("Atomics").call<unsigned int>("load", buffer, index);
+ 	}
+-	
++
+ 	void js_index_store(int8_t *sab_ptr, int8_t index, unsigned int value, size_t sab_size) {
+-		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr); 
++		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr);
+ 		val buffer = val(typed_memory_view(sab_size, buffer_view));
+-		
++
+ 		return val::global("Atomics").call<void>("store", buffer, index, value);
+ 	}
+ 
+ 	// careful when calling this, you cannot wait on any index
+ 	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
+ 	unsigned int js_index_wait(int8_t *sab_ptr, int8_t index, size_t sab_size) {
+-		int32_t *buffer_view = reinterpret_cast<int32_t *>(sab_ptr); 
++		int32_t *buffer_view = reinterpret_cast<int32_t *>(sab_ptr);
+ 		val buffer = val(typed_memory_view(sab_size, buffer_view));
+ 
+ 		return val::global("Atomics").call<unsigned int>("wait", buffer, index);
+@@ -164,7 +164,7 @@ namespace {
+ 		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+ 		bzero(sys->sab, sys->sab_size);
+ 	}
+-	
++
+ 	int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
+ 	{
+ 		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+@@ -187,7 +187,7 @@ namespace {
+ 	{
+ 		Flush(aout);
+ 	}
+-	
++
+ 	int audio_worklet_push (audio_output_t *aout, const int8_t *data, unsigned data_size) {
+ 		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+ 		int8_t *sab_view = sys->sab + 5 * sizeof(int32_t);
+@@ -199,7 +199,7 @@ namespace {
+ 			unsigned data_size_copy_end = STORAGE_SIZE - head;
+ 			memcpy(sab_view + head, data, data_size_copy_end);
+ 			head = 0;
+-			
++
+ 			// Copy the part of the data at the buffer start
+ 			unsigned data_size_copy_start = data_size - data_size_copy_end;
+ 			memcpy(sab_view + head, data, data_size_copy_start);
+@@ -219,7 +219,7 @@ namespace {
+ 		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+ 		const int8_t* data = (int8_t *)block->p_buffer;
+ 		size_t data_size = block->i_buffer;
+-		
++
+ 		unsigned head = js_index_load(sys->sab, 1, sys->sab_size);
+ 		unsigned tail = js_index_load(sys->sab, 2, sys->sab_size);
+ 		unsigned new_head = (head + data_size) % STORAGE_SIZE;
+@@ -232,7 +232,7 @@ namespace {
+ 		audio_worklet_push(aout, data, data_size);
+ 		block_Release(block);
+ 	}
+-	
++
+ 	void Pause( audio_output_t *aout, bool paused, vlc_tick_t date )
+ 	{
+ 		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+@@ -245,7 +245,7 @@ namespace {
+ 		VLC_UNUSED(date);
+ 		Flush(aout);
+ 	}
+-	
++
+ 	int Time_Get( audio_output_t *aout, vlc_tick_t *delay)
+ 	{
+ 		return aout_TimeGetDefault(aout, delay);
+@@ -289,16 +289,16 @@ namespace {
+ 		return 0;
+ 	}
+ 
+-	
++
+ 	int Open( vlc_object_t *obj )
+ 	{
+ 		audio_output_t * aout = (audio_output_t *) obj;
+-		
++
+ 		/* Allocate structures */
+ 		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(malloc( sizeof( *sys ) ));
+ 		if( unlikely(sys == NULL) )
+ 			return VLC_ENOMEM;
+-		
++
+ 		aout->sys = sys;
+ 		aout->start = Start;
+ 		aout->stop = Stop;
+@@ -308,7 +308,7 @@ namespace {
+ 		aout->time_get = Time_Get;
+ 		aout->volume_set = Volume_Set;
+ 		aout->mute_set = Mute_Set;
+-		
++
+ 		sys->awn_inst = new AWNodeWrapper(AUDIO_WORKLET_SAMPLE_RATE);
+ 		sys->sab_size = 5 * sizeof(int32_t) + STORAGE_SIZE;
+ 		sys->sab = reinterpret_cast<int8_t *>(malloc( sys->sab_size ));
+@@ -317,9 +317,9 @@ namespace {
+ 		if ( unlikely(sys->sab == NULL) )
+ 			return VLC_ENOMEM;
+ 		bzero(sys->sab, sys->sab_size);
+-		
++
+ 		val webaudio_context = sys->awn_inst->getCtx();
+-		
++
+ 		// Prepare audioWorkletProcessor blob
+ 		val document = val::global("document");
+ 		val script = document.call<val>("createElement", std::string("SCRIPT"));
+@@ -376,7 +376,7 @@ registerProcessor('worklet-processor', Processor);";
+ 		val BlobObject = val::object();
+ 		BlobObject.set("type", std::string("application/javascript"));
+ 		val WorkletModuleUrl = val::global("URL").call<val>("createObjectURL", val::global("Blob").new_(ProcessorTextArray, BlobObject));
+-		
++
+ 		// Prepare audioWorkletProcessor callback
+ 		val cb_caller = val::module_property("awn_cb_wrapper").new_(AUDIO_WORKLET_SAMPLE_RATE);
+ 		cb_caller.set("context", val(webaudio_context));
+@@ -385,7 +385,7 @@ registerProcessor('worklet-processor', Processor);";
+ 		cb_caller.set("channels", val(AUDIO_WORKLET_NB_CHANNELS));
+ 		val awn_caller = cb_caller["awn_call"];
+ 		val awn_cb = awn_caller.call<val>("bind", cb_caller);
+-		
++
+ 		// start audio worklet (since the context is suspended, sound won't start now
+ 		// Since the WebAudio Context cannot be created in a worker, we create
+ 		// it in the main_thread and use the SAB to signal it when we want it to start
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0002-Fix-emscripten-API-to-get-set-volume-levels-in-audio.patch b/vlc_patches/audio_output/0002-Fix-emscripten-API-to-get-set-volume-levels-in-audio.patch
new file mode 100644
index 0000000000000000000000000000000000000000..84713d3ebb316ffb901a02254d1d6434d25276c1
--- /dev/null
+++ b/vlc_patches/audio_output/0002-Fix-emscripten-API-to-get-set-volume-levels-in-audio.patch
@@ -0,0 +1,54 @@
+From 7438b4206722654dc3704d68393268b797a190a1 Mon Sep 17 00:00:00 2001
+From: Olivier FAURE <couteaubleu@gmail.com>
+Date: Sun, 30 May 2021 12:14:26 +0200
+Subject: [PATCH 2/5] Fix emscripten API to get/set volume levels in
+ audio_output module
+
+---
+ modules/audio_output/emscripten.cpp | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index b704b90699..3fefa60065 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -162,7 +162,7 @@ namespace {
+ 	void Flush( audio_output_t *aout )
+ 	{
+ 		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+-		bzero(sys->sab, sys->sab_size);
++		bzero(sys->sab + (5 * sizeof(int32_t)), STORAGE_SIZE);
+ 	}
+ 
+ 	int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
+@@ -177,7 +177,7 @@ namespace {
+ 		fmt->i_rate = AUDIO_WORKLET_SAMPLE_RATE;
+ 
+ 		// resume audio context (first start, it is paused when initialized)
+-		js_index_store(sys->sab, 4, (int)sys->volume * 100, sys->sab_size);
++		js_index_store(sys->sab, 4, (int)(sys->volume * 100), sys->sab_size);
+ 		js_index_store(sys->sab, 0, 1, sys->sab_size);
+ 
+ 		return VLC_SUCCESS;
+@@ -229,6 +229,10 @@ namespace {
+ 			// it will be notified by an Atomics.notify() from the process() callback
+ 			js_index_wait(sys->sab, 3, sys->sab_size);
+ 		}
++
++		// set volume
++		js_index_store(sys->sab, 4, (int)(sys->volume * 100), sys->sab_size);
++
+ 		audio_worklet_push(aout, data, data_size);
+ 		block_Release(block);
+ 	}
+@@ -271,7 +275,6 @@ namespace {
+ 			volume = 0.0f;
+ 		// TODO: implement gain
+ 		sys->volume = volume;
+-		js_index_store(sys->sab, 4, (int)volume * 100, sys->sab_size);
+ 		aout_VolumeReport(aout, volume);
+ 
+ 		return 0;
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0003-Move-some-audio-worklet-logic-out-of-JS-code.patch b/vlc_patches/audio_output/0003-Move-some-audio-worklet-logic-out-of-JS-code.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8b8e8c14230ffa0f356871679afc8f9ec2115710
--- /dev/null
+++ b/vlc_patches/audio_output/0003-Move-some-audio-worklet-logic-out-of-JS-code.patch
@@ -0,0 +1,84 @@
+From 997717c4cd8f341704061561188cddbc55e52ef3 Mon Sep 17 00:00:00 2001
+From: Olivier FAURE <couteaubleu@gmail.com>
+Date: Sun, 30 May 2021 14:18:32 +0200
+Subject: [PATCH 3/5] Move some audio worklet logic out of JS code.
+
+This isn't terribly useful on its own, but serves to prepare the next
+commit.
+---
+ modules/audio_output/emscripten.cpp | 35 ++++++++++++++++++-----------
+ 1 file changed, 22 insertions(+), 13 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index 3fefa60065..625dda30c0 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -86,12 +86,23 @@ namespace {
+ 			val AudioNode = val::global("AudioWorkletNode").new_(context, std::string("worklet-processor"), awn_options);
+ 			AudioNode.set("channelCount", channels);
+ 
++      val Uint32Array = val::global("Uint32Array");
++      val Int32Array = val::global("Int32Array");
++      val Float32Array = val::global("Float32Array");
++
++      auto wasm_memory_buffer = val::module_property("wasmMemory")["buffer"];
++      uint32_t storage_capacity = sab_size / 4;
++
+ 			//Prepare postMessage message
+ 			val msg = val::object();
+ 			msg.set("type", std::string("recv-audio-queue"));
+-			msg.set("data", val::module_property("wasmMemory")["buffer"]);
+-			msg.set("sab_ptr", sab_ptr);
+-			msg.set("sab_size", sab_size);
++
++			msg.set("flag", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 0, 1));
++			msg.set("head", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 4, 1));
++			msg.set("tail", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 8, 1));
++			msg.set("can_write", Int32Array.new_(wasm_memory_buffer, sab_ptr + 12, 1));
++			msg.set("volume", Int32Array.new_(wasm_memory_buffer, sab_ptr + 16, 1));
++			msg.set("storage", Float32Array.new_(wasm_memory_buffer, sab_ptr + 20, storage_capacity));
+ 
+ 			AudioNode["port"].call<val>("postMessage", msg);
+ 			AudioNode.call<val>("connect", context["destination"]);
+@@ -122,7 +133,7 @@ namespace {
+ 	} aout_sys_t;
+ 
+ 	EM_BOOL requestAnimationFrame_cb( double time, void *userData ) {
+-		(double) time;
++		(void) time;
+ 		AWNodeWrapper *inst = reinterpret_cast<AWNodeWrapper *>(userData);
+ 		uint32_t *sab = reinterpret_cast<uint32_t *>(inst->getSabPtr());
+ 		val view = val(typed_memory_view(inst->getSabSize(), sab));
+@@ -332,14 +343,12 @@ namespace {
+ 		super(); \
+ 		this.port.onmessage = e => { \
+ 			if (e.data.type === 'recv-audio-queue') { \
+-				this.buf = e.data.data; \
+-				this.capacity = e.data.sab_size / 4; \
+-				this.flag = new Uint32Array(this.buf, e.data.sab_ptr, 1); \
+-				this.head = new Uint32Array(this.buf, e.data.sab_ptr + 4, 1); \
+-				this.tail = new Uint32Array(this.buf, e.data.sab_ptr + 8, 1); \
+-				this.can_write = new Int32Array(this.buf, e.data.sab_ptr + 12, 1); \
+-				this.volume = new Int32Array(this.buf, e.data.sab_ptr + 16, 1); \
+-				this.storage = new Float32Array(this.buf, e.data.sab_ptr + 20, this.capacity); \
++				this.flag = e.data.flag; \
++				this.head = e.data.head; \
++				this.tail = e.data.tail; \
++				this.can_write = e.data.can_write; \
++				this.volume = e.data.volume; \
++				this.storage = e.data.storage; \
+ 			} else { \
+ 				throw 'unexpected.'; \
+ 			} \
+@@ -361,7 +370,7 @@ namespace {
+ 			for (let c = 0; c < nbChannels; ++c) { \
+ 				output[c][i] = this.storage[tail] * volume; \
+ 				tail++; \
+-				if (tail == this.capacity) { \
++				if (tail == this.storage.length) { \
+ 					tail = 0; \
+ 				} \
+ 			} \
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0004-Replace-untyped-js_index_load-store-functions-with-C.patch b/vlc_patches/audio_output/0004-Replace-untyped-js_index_load-store-functions-with-C.patch
new file mode 100644
index 0000000000000000000000000000000000000000..cab4a240a4a846caa45f0c30845ca7618fb4feb2
--- /dev/null
+++ b/vlc_patches/audio_output/0004-Replace-untyped-js_index_load-store-functions-with-C.patch
@@ -0,0 +1,291 @@
+From 9526900f5d809cb24f6d21b7a79cd3496ae0e999 Mon Sep 17 00:00:00 2001
+From: Olivier FAURE <couteaubleu@gmail.com>
+Date: Sun, 30 May 2021 15:15:22 +0200
+Subject: [PATCH 4/5] Replace untyped js_index_load/store functions with C++
+ atomics
+
+---
+ modules/audio_output/emscripten.cpp | 116 ++++++++++++++--------------
+ 1 file changed, 59 insertions(+), 57 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index 625dda30c0..fa0f5bd3e4 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -22,6 +22,8 @@
+ # include "config.h"
+ #endif
+ 
++#include <atomic>
++
+ #include <assert.h>
+ #include <vlc_common.h>
+ #include <vlc_plugin.h>
+@@ -45,6 +47,18 @@ using namespace emscripten;
+ namespace {
+ 	EM_BOOL requestAnimationFrame_cb( double time, void *userData );
+ 
++	typedef struct sound_buffer_t
++	{
++    // TODO - should be bool?
++    std::atomic<uint32_t> is_paused;
++    std::atomic<uint32_t> head;
++    std::atomic<uint32_t> tail;
++    std::atomic<uint32_t> can_write;
++    std::atomic<uint32_t> volume;
++    int8_t storage[STORAGE_SIZE];
++
++	} sound_buffer_t;
++
+ 	class AWNodeWrapper {
+ 	public:
+ 		val context = val::undefined();
+@@ -55,10 +69,6 @@ namespace {
+ 		uintptr_t getSabPtr() const { return sab_ptr; };
+ 		void setSabPtr(uintptr_t p_sab) { sab_ptr = p_sab; };
+ 
+-		size_t sab_size;
+-		size_t getSabSize() const { return sab_size; };
+-		void setSabSize(size_t s_size) { sab_size = s_size; };
+-
+ 		int8_t channels;
+ 		int8_t getChannels() const { return channels; };
+ 		void setChannels(int8_t chan) { channels = chan; };
+@@ -90,19 +100,26 @@ namespace {
+       val Int32Array = val::global("Int32Array");
+       val Float32Array = val::global("Float32Array");
+ 
+-      auto wasm_memory_buffer = val::module_property("wasmMemory")["buffer"];
+-      uint32_t storage_capacity = sab_size / 4;
++      auto wasm_mem = val::module_property("wasmMemory")["buffer"];
+ 
+ 			//Prepare postMessage message
+ 			val msg = val::object();
+ 			msg.set("type", std::string("recv-audio-queue"));
+ 
+-			msg.set("flag", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 0, 1));
+-			msg.set("head", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 4, 1));
+-			msg.set("tail", Uint32Array.new_(wasm_memory_buffer, sab_ptr + 8, 1));
+-			msg.set("can_write", Int32Array.new_(wasm_memory_buffer, sab_ptr + 12, 1));
+-			msg.set("volume", Int32Array.new_(wasm_memory_buffer, sab_ptr + 16, 1));
+-			msg.set("storage", Float32Array.new_(wasm_memory_buffer, sab_ptr + 20, storage_capacity));
++      msg.set("is_paused",
++        Uint32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, is_paused), 1));
++      msg.set("head",
++        Uint32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, head), 1));
++      msg.set("tail",
++        Uint32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, tail), 1));
++      msg.set("can_write",
++        Int32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, can_write), 1));
++      msg.set("volume",
++        Int32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, volume), 1));
++
++      uint32_t storage_capacity = STORAGE_SIZE / 4;
++      msg.set("storage",
++        Float32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, storage), storage_capacity));
+ 
+ 			AudioNode["port"].call<val>("postMessage", msg);
+ 			AudioNode.call<val>("connect", context["destination"]);
+@@ -118,25 +135,25 @@ namespace {
+ 			.constructor<int>()
+ 			.property("context", &AWNodeWrapper::getCtx, &AWNodeWrapper::setCtx)
+ 			.property("sab_ptr", &AWNodeWrapper::getSabPtr, &AWNodeWrapper::setSabPtr)
+-			.property("sab_size", &AWNodeWrapper::getSabSize, &AWNodeWrapper::setSabSize)
+ 			.property("channels", &AWNodeWrapper::getChannels, &AWNodeWrapper::setChannels)
+ 			.function("awn_call", &AWNodeWrapper::operator());
+ 	};
+ 
+ 	typedef struct aout_sys_t
+ 	{
+-		int8_t *sab;
+-		size_t sab_size;
++		sound_buffer_t *sab; // TODO - rename to sound_buff
+ 		AWNodeWrapper *awn_inst;
+ 		float volume;
+ 
+ 	} aout_sys_t;
+ 
+ 	EM_BOOL requestAnimationFrame_cb( double time, void *userData ) {
+-		(void) time;
++		VLC_UNUSED(time);
++    // FIXME - this function seems to mix two different views on the
++    // same memory, not sure why
+ 		AWNodeWrapper *inst = reinterpret_cast<AWNodeWrapper *>(userData);
+ 		uint32_t *sab = reinterpret_cast<uint32_t *>(inst->getSabPtr());
+-		val view = val(typed_memory_view(inst->getSabSize(), sab));
++		val view = val(typed_memory_view(sizeof(sound_buffer_t), sab));
+ 		val context = inst->getCtx();
+ 		if ( view[0].as<int>() == 1 ) {
+ 			context.call<val>("resume");
+@@ -146,34 +163,19 @@ namespace {
+ 		return EM_TRUE;
+ 	}
+ 
+-	// For Atomics.store() and .load() only integer types are supported
+-	unsigned int js_index_load(int8_t *sab_ptr, int8_t index, size_t sab_size){
+-		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr);
+-		val buffer = val(typed_memory_view(sab_size, buffer_view));
+-
+-		return val::global("Atomics").call<unsigned int>("load", buffer, index);
+-	}
+-
+-	void js_index_store(int8_t *sab_ptr, int8_t index, unsigned int value, size_t sab_size) {
+-		uint32_t *buffer_view = reinterpret_cast<uint32_t *>(sab_ptr);
+-		val buffer = val(typed_memory_view(sab_size, buffer_view));
+-
+-		return val::global("Atomics").call<void>("store", buffer, index, value);
+-	}
+-
+ 	// careful when calling this, you cannot wait on any index
+ 	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
+-	unsigned int js_index_wait(int8_t *sab_ptr, int8_t index, size_t sab_size) {
++	uint32_t js_index_wait(sound_buffer_t *sab_ptr, int8_t index) {
+ 		int32_t *buffer_view = reinterpret_cast<int32_t *>(sab_ptr);
+-		val buffer = val(typed_memory_view(sab_size, buffer_view));
++		val buffer = val(typed_memory_view(STORAGE_SIZE, buffer_view));
+ 
+-		return val::global("Atomics").call<unsigned int>("wait", buffer, index);
++		return val::global("Atomics").call<uint32_t>("wait", buffer, index);
+ 	}
+ 
+ 	void Flush( audio_output_t *aout )
+ 	{
+ 		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+-		bzero(sys->sab + (5 * sizeof(int32_t)), STORAGE_SIZE);
++		bzero(&sys->sab->storage, sizeof(sys->sab->storage));
+ 	}
+ 
+ 	int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
+@@ -188,8 +190,8 @@ namespace {
+ 		fmt->i_rate = AUDIO_WORKLET_SAMPLE_RATE;
+ 
+ 		// resume audio context (first start, it is paused when initialized)
+-		js_index_store(sys->sab, 4, (int)(sys->volume * 100), sys->sab_size);
+-		js_index_store(sys->sab, 0, 1, sys->sab_size);
++		sys->sab->volume.store((int)(sys->volume * 100));
++		sys->sab->is_paused.store(1);
+ 
+ 		return VLC_SUCCESS;
+ 	}
+@@ -199,10 +201,10 @@ namespace {
+ 		Flush(aout);
+ 	}
+ 
+-	int audio_worklet_push (audio_output_t *aout, const int8_t *data, unsigned data_size) {
++	int audio_worklet_push (audio_output_t *aout, const int8_t *data, uint32_t data_size) {
+ 		aout_sys_t *sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+-		int8_t *sab_view = sys->sab + 5 * sizeof(int32_t);
+-		unsigned head = js_index_load(sys->sab, 1, sys->sab_size);
++		int8_t *sab_view = sys->sab->storage;
++		uint32_t head = sys->sab->head.load();
+ 
+ 		// TODO: check that we do not write on unconsumed data.
+ 		if (head + data_size > STORAGE_SIZE) {
+@@ -220,7 +222,7 @@ namespace {
+ 			memcpy(sab_view + head, data, data_size);
+ 			head += data_size;
+ 		}
+-		js_index_store(sys->sab, 1, head, sys->sab_size);
++		sys->sab->head.store(head);
+ 		return 0;  // return success to indicate successful push.
+ 	}
+ 
+@@ -231,18 +233,18 @@ namespace {
+ 		const int8_t* data = (int8_t *)block->p_buffer;
+ 		size_t data_size = block->i_buffer;
+ 
+-		unsigned head = js_index_load(sys->sab, 1, sys->sab_size);
+-		unsigned tail = js_index_load(sys->sab, 2, sys->sab_size);
+-		unsigned new_head = (head + data_size) % STORAGE_SIZE;
++		uint32_t head = sys->sab->head.load();
++		uint32_t tail = sys->sab->tail.load();
++		uint32_t new_head = (head + data_size) % STORAGE_SIZE;
+ 		if (new_head > tail)
+ 		{
+ 			// the worklet processor keeps rendering  until tail matches head
+ 			// it will be notified by an Atomics.notify() from the process() callback
+-			js_index_wait(sys->sab, 3, sys->sab_size);
++			js_index_wait(sys->sab, 3);
+ 		}
+ 
+ 		// set volume
+-		js_index_store(sys->sab, 4, (int)(sys->volume * 100), sys->sab_size);
++		sys->sab->volume.store((int)(sys->volume * 100));
+ 
+ 		audio_worklet_push(aout, data, data_size);
+ 		block_Release(block);
+@@ -250,14 +252,14 @@ namespace {
+ 
+ 	void Pause( audio_output_t *aout, bool paused, vlc_tick_t date )
+ 	{
++		VLC_UNUSED(date);
+ 		aout_sys_t * sys = reinterpret_cast<aout_sys_t *>(aout->sys);
+ 		if (paused == false) {
+-			js_index_store(sys->sab, 0, 0, sys->sab_size);
++			sys->sab->is_paused.store(0);
+ 		}
+ 		else {
+-			js_index_store(sys->sab, 0, 1, sys->sab_size);
++			sys->sab->is_paused.store(1);
+ 		}
+-		VLC_UNUSED(date);
+ 		Flush(aout);
+ 	}
+ 
+@@ -271,6 +273,7 @@ namespace {
+ 		audio_output_t *aout = (audio_output_t *)obj;
+ 		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
+ 
++    // FIXME
+ 		delete sys->awn_inst;
+ 		free(sys->sab);
+ 		free(sys);
+@@ -296,9 +299,10 @@ namespace {
+ 		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
+ 
+ 		if (mute == 0)
+-			js_index_store(sys->sab, 4, 0, sys->sab_size);
++			sys->sab->volume.store(0);
+ 		else
+-			js_index_store(sys->sab, 4, (int)sys->volume * 100, sys->sab_size);
++			sys->sab->volume.store((int)(sys->volume * 100));
++
+ 		aout_MuteReport(aout, mute);
+ 		return 0;
+ 	}
+@@ -324,13 +328,12 @@ namespace {
+ 		aout->mute_set = Mute_Set;
+ 
+ 		sys->awn_inst = new AWNodeWrapper(AUDIO_WORKLET_SAMPLE_RATE);
+-		sys->sab_size = 5 * sizeof(int32_t) + STORAGE_SIZE;
+-		sys->sab = reinterpret_cast<int8_t *>(malloc( sys->sab_size ));
++		sys->sab = (sound_buffer_t*)malloc(sizeof(sound_buffer_t));
+ 		sys->volume = 1.0f;
+ 
+ 		if ( unlikely(sys->sab == NULL) )
+ 			return VLC_ENOMEM;
+-		bzero(sys->sab, sys->sab_size);
++		bzero(sys->sab, sizeof(sound_buffer_t));
+ 
+ 		val webaudio_context = sys->awn_inst->getCtx();
+ 
+@@ -343,7 +346,7 @@ namespace {
+ 		super(); \
+ 		this.port.onmessage = e => { \
+ 			if (e.data.type === 'recv-audio-queue') { \
+-				this.flag = e.data.flag; \
++				this.is_paused = e.data.is_paused; \
+ 				this.head = e.data.head; \
+ 				this.tail = e.data.tail; \
+ 				this.can_write = e.data.can_write; \
+@@ -393,7 +396,6 @@ registerProcessor('worklet-processor', Processor);";
+ 		val cb_caller = val::module_property("awn_cb_wrapper").new_(AUDIO_WORKLET_SAMPLE_RATE);
+ 		cb_caller.set("context", val(webaudio_context));
+ 		cb_caller.set("sab_ptr", val(reinterpret_cast<uintptr_t>(sys->sab)));
+-		cb_caller.set("sab_size", val(sys->sab_size));
+ 		cb_caller.set("channels", val(AUDIO_WORKLET_NB_CHANNELS));
+ 		val awn_caller = cb_caller["awn_call"];
+ 		val awn_cb = awn_caller.call<val>("bind", cb_caller);
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0005-Fix-volume-handling-in-emscripten-audio_output-modul.patch b/vlc_patches/audio_output/0005-Fix-volume-handling-in-emscripten-audio_output-modul.patch
new file mode 100644
index 0000000000000000000000000000000000000000..79f1db48647d7a466f32cd3ad2639a8c64389ef8
--- /dev/null
+++ b/vlc_patches/audio_output/0005-Fix-volume-handling-in-emscripten-audio_output-modul.patch
@@ -0,0 +1,124 @@
+From 39c492904740f6bdcf794e0359930f01c49ee725 Mon Sep 17 00:00:00 2001
+From: Olivier FAURE <couteaubleu@gmail.com>
+Date: Sun, 30 May 2021 17:18:23 +0200
+Subject: [PATCH 5/5] Fix volume handling in emscripten audio_output module
+
+Fix bug that had volume divided by 4
+Store mute state separately
+---
+ modules/audio_output/emscripten.cpp | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index fa0f5bd3e4..c39ca884b6 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -55,6 +55,7 @@ namespace {
+     std::atomic<uint32_t> tail;
+     std::atomic<uint32_t> can_write;
+     std::atomic<uint32_t> volume;
++    std::atomic<uint32_t> is_muted;
+     int8_t storage[STORAGE_SIZE];
+ 
+ 	} sound_buffer_t;
+@@ -116,6 +117,8 @@ namespace {
+         Int32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, can_write), 1));
+       msg.set("volume",
+         Int32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, volume), 1));
++      msg.set("is_muted",
++        Uint32Array.new_(wasm_mem, sab_ptr + offsetof(sound_buffer_t, is_muted), 1));
+ 
+       uint32_t storage_capacity = STORAGE_SIZE / 4;
+       msg.set("storage",
+@@ -143,7 +146,6 @@ namespace {
+ 	{
+ 		sound_buffer_t *sab; // TODO - rename to sound_buff
+ 		AWNodeWrapper *awn_inst;
+-		float volume;
+ 
+ 	} aout_sys_t;
+ 
+@@ -190,7 +192,6 @@ namespace {
+ 		fmt->i_rate = AUDIO_WORKLET_SAMPLE_RATE;
+ 
+ 		// resume audio context (first start, it is paused when initialized)
+-		sys->sab->volume.store((int)(sys->volume * 100));
+ 		sys->sab->is_paused.store(1);
+ 
+ 		return VLC_SUCCESS;
+@@ -240,12 +241,10 @@ namespace {
+ 		{
+ 			// the worklet processor keeps rendering  until tail matches head
+ 			// it will be notified by an Atomics.notify() from the process() callback
++      // FIXME - This is layout-dependent, which isn't ideal
+ 			js_index_wait(sys->sab, 3);
+ 		}
+ 
+-		// set volume
+-		sys->sab->volume.store((int)(sys->volume * 100));
+-
+ 		audio_worklet_push(aout, data, data_size);
+ 		block_Release(block);
+ 	}
+@@ -288,7 +287,9 @@ namespace {
+ 		else if (volume < 0.0f)
+ 			volume = 0.0f;
+ 		// TODO: implement gain
+-		sys->volume = volume;
++		// Note: We store volume as an integer between 0..100 because
++		// for some reason Float32Array doesn't allow atomic operations
++		sys->sab->volume.store((int)(volume * 100));
+ 		aout_VolumeReport(aout, volume);
+ 
+ 		return 0;
+@@ -298,12 +299,9 @@ namespace {
+ 	{
+ 		struct aout_sys_t *sys = reinterpret_cast<struct aout_sys_t *>(aout->sys);
+ 
+-		if (mute == 0)
+-			sys->sab->volume.store(0);
+-		else
+-			sys->sab->volume.store((int)(sys->volume * 100));
+-
++		sys->sab->is_muted.store(mute);
+ 		aout_MuteReport(aout, mute);
++
+ 		return 0;
+ 	}
+ 
+@@ -329,11 +327,11 @@ namespace {
+ 
+ 		sys->awn_inst = new AWNodeWrapper(AUDIO_WORKLET_SAMPLE_RATE);
+ 		sys->sab = (sound_buffer_t*)malloc(sizeof(sound_buffer_t));
+-		sys->volume = 1.0f;
+ 
+ 		if ( unlikely(sys->sab == NULL) )
+ 			return VLC_ENOMEM;
+ 		bzero(sys->sab, sizeof(sound_buffer_t));
++		sys->sab->volume = 100;
+ 
+ 		val webaudio_context = sys->awn_inst->getCtx();
+ 
+@@ -351,6 +349,7 @@ namespace {
+ 				this.tail = e.data.tail; \
+ 				this.can_write = e.data.can_write; \
+ 				this.volume = e.data.volume; \
++				this.is_muted = e.data.is_muted; \
+ 				this.storage = e.data.storage; \
+ 			} else { \
+ 				throw 'unexpected.'; \
+@@ -367,7 +366,10 @@ namespace {
+ 		var head = Atomics.load(this.head, 0) / 4; \
+ 		var tail = Atomics.load(this.tail, 0) / 4; \
+ 		var i = 0; \
+-		var volume = (Atomics.load(this.volume, 0) / 4) / 100; \
++		var volume = Atomics.load(this.volume, 0) / 100; \
++		if (Atomics.load(this.is_paused, 0) != 0 || Atomics.load(this.is_muted, 0) != 0) { \
++			volume = 0; \
++		} \
+ 		while (tail != head && i < nbSamples) \
+ 		{ \
+ 			for (let c = 0; c < nbChannels; ++c) { \
+-- 
+2.32.0
+
diff --git a/vlc_patches/audio_output/0006-aout-fix-js_index_wait.patch b/vlc_patches/audio_output/0006-aout-fix-js_index_wait.patch
new file mode 100644
index 0000000000000000000000000000000000000000..93a5ee04ec22016ab4f768ee1e66173b2d1b19c0
--- /dev/null
+++ b/vlc_patches/audio_output/0006-aout-fix-js_index_wait.patch
@@ -0,0 +1,51 @@
+From 825cfa2a712df088213bb806ba8868623020aa64 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Thu, 1 Jul 2021 11:28:53 +0200
+Subject: [PATCH 1/1] aout: fix js_index_wait
+
+- fix return type
+- notifying without changing the value won't update the value
+---
+ modules/audio_output/emscripten.cpp | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/modules/audio_output/emscripten.cpp b/modules/audio_output/emscripten.cpp
+index c39ca884b6..6acea387e3 100644
+--- a/modules/audio_output/emscripten.cpp
++++ b/modules/audio_output/emscripten.cpp
+@@ -167,11 +167,11 @@ namespace {
+ 
+ 	// careful when calling this, you cannot wait on any index
+ 	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
+-	uint32_t js_index_wait(sound_buffer_t *sab_ptr, int8_t index) {
++	void js_index_wait(sound_buffer_t *sab_ptr, int8_t index) {
+ 		int32_t *buffer_view = reinterpret_cast<int32_t *>(sab_ptr);
+ 		val buffer = val(typed_memory_view(STORAGE_SIZE, buffer_view));
+ 
+-		return val::global("Atomics").call<uint32_t>("wait", buffer, index);
++		val::global("Atomics").call<val>("wait", buffer, index, 0);
+ 	}
+ 
+ 	void Flush( audio_output_t *aout )
+@@ -241,7 +241,7 @@ namespace {
+ 		{
+ 			// the worklet processor keeps rendering  until tail matches head
+ 			// it will be notified by an Atomics.notify() from the process() callback
+-      // FIXME - This is layout-dependent, which isn't ideal
++			// FIXME - This is layout-dependent, which isn't ideal
+ 			js_index_wait(sys->sab, 3);
+ 		}
+ 
+@@ -382,7 +382,8 @@ namespace {
+ 			i++; \
+ 		} \
+ 		Atomics.store(this.tail, 0, tail * 4); \
+-		Atomics.notify(this.can_write, 0); \
++		Atomics.store(this.can_write, 0, 1); \
++		Atomics.notify(this.can_write, 0);   \
+ 		return true; \
+ 	} \
+ } \
+-- 
+2.32.0
+
diff --git a/vlc_patches/dav1d/0001-contrib-bump-dav1d-to-0.5.0.patch b/vlc_patches/dav1d/0001-contrib-bump-dav1d-to-0.5.0.patch
deleted file mode 100644
index d81906bf7bd0d92cb46b07cff697da4b92e227af..0000000000000000000000000000000000000000
--- a/vlc_patches/dav1d/0001-contrib-bump-dav1d-to-0.5.0.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 9bed6fe6444a86a6660d275e237bb9126942c471 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 24 Oct 2019 18:05:16 +0200
-Subject: [PATCH 1/2] contrib: bump dav1d to 0.5.0
-
----
- contrib/src/dav1d/SHA512SUMS | 2 +-
- contrib/src/dav1d/rules.mak  | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/contrib/src/dav1d/SHA512SUMS b/contrib/src/dav1d/SHA512SUMS
-index b3b14ff506..90996861fc 100644
---- a/contrib/src/dav1d/SHA512SUMS
-+++ b/contrib/src/dav1d/SHA512SUMS
-@@ -1 +1 @@
--8ed44b3d747f01b87b34f86fada824dfb7f86c16168af641fe754c767af5714e9fe212b6eea2bc11b5b041460184c78f755e10d4947e46bc70d95e1bd750f79d  dav1d-0.4.0.tar.xz
-+10d3b174fa7ade0fb56f354dec8218761291c69d1ce1825d04bb6c7f6b28581f8811fd6e65c0f2a7a609b9694c11138ef469a2447814e020c038dba8d04de4df  dav1d-0.5.0.tar.xz
-diff --git a/contrib/src/dav1d/rules.mak b/contrib/src/dav1d/rules.mak
-index 74a44dbb60..dbae572a02 100644
---- a/contrib/src/dav1d/rules.mak
-+++ b/contrib/src/dav1d/rules.mak
-@@ -1,6 +1,6 @@
- # libdav1d
- 
--DAV1D_VERSION := 0.4.0
-+DAV1D_VERSION := 0.5.0
- DAV1D_URL := $(VIDEOLAN)/dav1d/$(DAV1D_VERSION)/dav1d-$(DAV1D_VERSION).tar.xz
- #~ DAV1D_HASH := de2059a1167ed560269c3253768929ef19cae989
- #~ DAV1D_VERSION := git-$(DAV1D_HASH)
--- 
-2.23.0
-
diff --git a/vlc_patches/dav1d/0002-contrib-add-dav1d-emscripten-support.patch b/vlc_patches/dav1d/0002-contrib-add-dav1d-emscripten-support.patch
deleted file mode 100644
index 29ad1f34a16b14b211a02e91dad0109c97d00a8f..0000000000000000000000000000000000000000
--- a/vlc_patches/dav1d/0002-contrib-add-dav1d-emscripten-support.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From f027bb628d1542be954b9aa580dad6e14810f03d Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 24 Oct 2019 18:06:52 +0200
-Subject: [PATCH 2/2] contrib: add dav1d emscripten support
-
----
- .../dav1d/0001-add-emscripten-support.patch   | 26 +++++++++++++++++++
- contrib/src/dav1d/rules.mak                   |  1 +
- 2 files changed, 27 insertions(+)
- create mode 100644 contrib/src/dav1d/0001-add-emscripten-support.patch
-
-diff --git a/contrib/src/dav1d/0001-add-emscripten-support.patch b/contrib/src/dav1d/0001-add-emscripten-support.patch
-new file mode 100644
-index 0000000000..9e63e5219a
---- /dev/null
-+++ b/contrib/src/dav1d/0001-add-emscripten-support.patch
-@@ -0,0 +1,26 @@
-+From fd9f38380eaf20b812091fbf6c2cea477c38d8e6 Mon Sep 17 00:00:00 2001
-+From: Mehdi Sabwat <mehdisabwat@gmail.com>
-+Date: Thu, 24 Oct 2019 14:05:31 +0200
-+Subject: [PATCH 1/1] add emscripten support
-+
-+---
-+ meson.build | 3 +++
-+ 1 file changed, 3 insertions(+)
-+
-+diff --git a/meson.build b/meson.build
-+index 90899a7..cc95315 100644
-+--- a/meson.build
-++++ b/meson.build
-+@@ -112,6 +112,9 @@ if host_machine.system() == 'windows'
-+     # On Windows, we use a compatibility layer to emulate pthread
-+     thread_dependency = []
-+     thread_compat_dep = declare_dependency(sources : files('src/win32/thread.c'))
-++elif host_machine.system() == 'emscripten'
-++    thread_dependency = []
-++    thread_compat_dep = []
-+ else
-+     thread_dependency = dependency('threads')
-+     thread_compat_dep = []
-+-- 
-+2.23.0
-+
-diff --git a/contrib/src/dav1d/rules.mak b/contrib/src/dav1d/rules.mak
-index dbae572a02..e3cf065b0b 100644
---- a/contrib/src/dav1d/rules.mak
-+++ b/contrib/src/dav1d/rules.mak
-@@ -24,6 +24,7 @@ $(TARBALLS)/dav1d-$(DAV1D_VERSION).tar.xz:
- 
- dav1d: dav1d-$(DAV1D_VERSION).tar.xz .sum-dav1d
- 	$(UNPACK)
-+	$(APPLY) $(SRC)/dav1d/0001-add-emscripten-support.patch
- 	$(MOVE)
- 
- .dav1d: dav1d crossfile.meson
--- 
-2.23.0
-
diff --git a/vlc_patches/filesystem/0001-access-initial-emscripten-file-api-support.patch b/vlc_patches/filesystem/0001-access-initial-emscripten-file-api-support.patch
new file mode 100644
index 0000000000000000000000000000000000000000..231c512226e0cbb707789d8f520a9cb9a535e5b0
--- /dev/null
+++ b/vlc_patches/filesystem/0001-access-initial-emscripten-file-api-support.patch
@@ -0,0 +1,205 @@
+From ffa3b9e16691a72b744715564f63b34ea57ab414 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Wed, 18 Nov 2020 17:50:39 +0100
+Subject: [PATCH 1/1] access: initial emscripten file api support
+
+---
+ modules/access/Makefile.am    |   8 ++
+ modules/access/emscripten.cpp | 169 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 177 insertions(+)
+ create mode 100644 modules/access/emscripten.cpp
+
+diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
+index 9cd3098fc2..331de24f82 100644
+--- a/modules/access/Makefile.am
++++ b/modules/access/Makefile.am
+@@ -454,3 +454,11 @@ librist_plugin_la_LIBADD = $(SOCKET_LIBS)
+ if HAVE_BITSTREAM
+ access_LTLIBRARIES += librist_plugin.la
+ endif
++
++### EMSCRIPTEN ###
++
++libemscriptenfs_plugin_la_SOURCES = access/emscripten.cpp
++libemscriptenfs_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
++if HAVE_EMSCRIPTEN
++access_LTLIBRARIES += libemscriptenfs_plugin.la
++endif
+diff --git a/modules/access/emscripten.cpp b/modules/access/emscripten.cpp
+new file mode 100644
+index 0000000000..f3162574f9
+--- /dev/null
++++ b/modules/access/emscripten.cpp
+@@ -0,0 +1,169 @@
++/*****************************************************************************
++ * emscripten.cpp: emscripten file system access plugin
++ *****************************************************************************
++ * Copyright (C) 2001-2020 VLC authors and VideoLAN
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as published by
++ * the Free Software Foundation; either version 2.1 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ *****************************************************************************/
++
++#ifdef HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <vlc_common.h>
++#include <vlc_plugin.h>
++#include<vlc_stream.h>
++#include <emscripten.h>
++
++static ssize_t Read (stream_t *, void *, size_t);
++static int FileSeek (stream_t *, uint64_t);
++static int FileControl (stream_t *, int, va_list);
++
++typedef struct
++{
++    int fd;
++    int offset;
++
++    bool b_pace_control;
++} access_sys_t;
++
++/*****************************************************************************
++ * FileOpen: open the file
++ *****************************************************************************/
++int FileOpen( vlc_object_t *p_this )
++{
++    stream_t     *p_access = reinterpret_cast<stream_t *>(p_this);
++
++    /* Open file */
++    int fd = 42; // not implemented for now
++    access_sys_t *p_sys = reinterpret_cast<access_sys_t *>(vlc_obj_malloc(p_this, sizeof (*p_sys)));
++    if (unlikely(p_sys == NULL))
++        goto error;
++    p_access->pf_read = Read;
++    p_access->pf_block = NULL;
++    p_access->pf_control = FileControl;
++    p_access->p_sys = p_sys;
++    p_sys->fd = fd;
++    p_sys->offset = 0;
++    p_access->pf_seek = FileSeek;
++    p_sys->b_pace_control = strcasecmp (p_access->psz_name, "stream");
++
++    return VLC_SUCCESS;
++error:
++    p_sys->offset=0;
++    return VLC_EGENERIC;
++}
++
++/*****************************************************************************
++ * FileClose: reset the offset to 0
++ *****************************************************************************/
++void FileClose (vlc_object_t * p_this)
++{
++    stream_t     *p_access = reinterpret_cast<stream_t *>(p_this);
++
++    access_sys_t *sys = reinterpret_cast<access_sys_t *>(p_access->p_sys);
++    sys->offset = 0;
++}
++
++static ssize_t Read (stream_t *p_access, void *p_buffer, size_t i_len)
++{
++    access_sys_t *sys = reinterpret_cast<access_sys_t *>(p_access->p_sys);
++
++    EM_ASM ({
++            let p_buffer = $0;
++            let start = $1;
++            let end = $2;
++
++            let reader = new FileReaderSync();
++            // TODO: get MimeType from the file handler
++            let data = reader.readAsArrayBuffer( new Blob([Module.fileHandle.slice(start, start+end)], {type: 'video/mp4'}) );
++            let dataView = new Uint8Array( data );
++            let wasmMemoryView = new Uint8Array( Module.wasmMemory.buffer, p_buffer, end); // ajouter + i_len
++            let wasmMemoryItems = wasmMemoryView.entries();
++            
++            for ( let item of dataView.entries() ) {
++                let index = wasmMemoryItems.next().value[0];
++                wasmMemoryView[index] = item[1];// an item is: [index, value]
++            }
++        }, p_buffer, sys->offset, i_len );
++    sys->offset += i_len;
++    return i_len;
++}
++
++/*****************************************************************************
++ * Seek: seek to a specific location in a file
++ *****************************************************************************/
++static int FileSeek (stream_t *p_access, uint64_t i_pos)
++{
++    access_sys_t *sys = reinterpret_cast<access_sys_t *>(p_access->p_sys);
++
++    // SEEK_SET: i_pos is an absolute position
++    sys->offset = i_pos;
++
++    return VLC_SUCCESS;
++}
++
++/*****************************************************************************
++ * Control:
++ *****************************************************************************/
++static int FileControl( stream_t *p_access, int i_query, va_list args )
++{
++    access_sys_t *sys = reinterpret_cast<access_sys_t *>(p_access->p_sys);
++    bool    *pb_bool;
++    vlc_tick_t *pi_64;
++    
++    switch( i_query )
++    {
++        case STREAM_CAN_SEEK:
++        case STREAM_CAN_FASTSEEK:
++            pb_bool = va_arg( args, bool * );
++            *pb_bool = false;
++            break;
++        case STREAM_CAN_PAUSE:
++        case STREAM_CAN_CONTROL_PACE:
++            pb_bool = va_arg( args, bool * );
++            *pb_bool = sys->b_pace_control;
++            break;
++        case STREAM_GET_SIZE:
++        {
++            size_t size = EM_ASM_INT({ return Module.fileHandle.size});
++            //printf("taille: %zu\n", size);
++            // abort();
++            *va_arg( args, uint64_t * ) = size;
++            break;
++        }
++        case STREAM_GET_PTS_DELAY:
++            pi_64 = va_arg( args, vlc_tick_t * );
++            // network caching for later
++            *pi_64 = VLC_TICK_FROM_MS(
++                var_InheritInteger (p_access, "file-caching") );
++            break;
++        default:
++            return VLC_EGENERIC;
++
++    }
++    return VLC_SUCCESS;
++}
++
++vlc_module_begin ()
++    set_description( N_("File input") )
++    set_shortname( N_("emscriptenfs") )
++    set_category( CAT_INPUT )
++    set_subcategory( SUBCAT_INPUT_ACCESS )
++    set_capability( "access", 50 )
++    add_shortcut( "emscriptenfs" )
++    set_callbacks( FileOpen, FileClose )
++vlc_module_end ()
+-- 
+2.32.0
+
diff --git a/vlc_patches/0012-logger-add-emscripten-module.patch b/vlc_patches/logger/0001-logger-add-emscripten-module.patch
similarity index 53%
rename from vlc_patches/0012-logger-add-emscripten-module.patch
rename to vlc_patches/logger/0001-logger-add-emscripten-module.patch
index 43e5e0b7e6af6eaa74079668fd72954aca65c991..02f0b1f2aca6864b8333083848e95480168c1b37 100644
--- a/vlc_patches/0012-logger-add-emscripten-module.patch
+++ b/vlc_patches/logger/0001-logger-add-emscripten-module.patch
@@ -1,37 +1,36 @@
-From bde1b91e748fa7b2a97041605628ab7c96bd6a61 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Mon, 9 Sep 2019 19:18:36 +0200
-Subject: [PATCH 12/15] logger: add emscripten module
+From 58c9af72a3cc04f39700369fbeb5579c0c384221 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Mon, 9 Nov 2020 22:35:32 +0100
+Subject: [PATCH] logger: add emscripten module
 
 ---
- modules/logger/Makefile.am  |  7 ++++
- modules/logger/emscripten.c | 82 +++++++++++++++++++++++++++++++++++++
- 2 files changed, 89 insertions(+)
+ modules/logger/Makefile.am  |  6 +++
+ modules/logger/emscripten.c | 92 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 98 insertions(+)
  create mode 100644 modules/logger/emscripten.c
 
 diff --git a/modules/logger/Makefile.am b/modules/logger/Makefile.am
-index 4addf6dcfd..9b26a68afd 100644
+index 8ab485c8fe..1d10a6e3a4 100644
 --- a/modules/logger/Makefile.am
 +++ b/modules/logger/Makefile.am
-@@ -22,3 +22,10 @@ libandroid_logger_plugin_la_LIBADD = -llog
- if HAVE_ANDROID
- logger_LTLIBRARIES += libandroid_logger_plugin.la
- endif
+@@ -25,3 +25,9 @@ endif
+ 
+ libjson_tracer_plugin_la_SOURCES = logger/json.c
+ logger_LTLIBRARIES += libjson_tracer_plugin.la
 +
 +libemscripten_logger_plugin_la_SOURCES = logger/emscripten.c
-+libemscripten_logger_plugin_la_CFLAGS = $(AM_CFLAGS)
 +
 +if HAVE_EMSCRIPTEN
 +logger_LTLIBRARIES += libemscripten_logger_plugin.la
 +endif
 diff --git a/modules/logger/emscripten.c b/modules/logger/emscripten.c
 new file mode 100644
-index 0000000000..bb7ef220de
+index 0000000000..85fdedb0d6
 --- /dev/null
 +++ b/modules/logger/emscripten.c
-@@ -0,0 +1,82 @@
+@@ -0,0 +1,92 @@
 +/*****************************************************************************
-+ * emscripten.c: Android logger using logcat
++ * emscripten.c: Emscripten logger
 + *****************************************************************************
 + * Copyright © 2019 VLC authors and VideoLAN
 + *
@@ -63,55 +62,65 @@ index 0000000000..bb7ef220de
 +#include <vlc_plugin.h>
 +
 +static void EmscriptenPrintMsg(void *opaque, int type, const vlc_log_t *p_item,
-+                               const char *format, va_list ap)
++							   const char *format, va_list ap)
 +{
-+    int prio;
-+    char *format2;
-+    int verbose = (intptr_t)opaque;
++	int prio;
++	char *new_format;
++	char *message;
++	int verbose = (intptr_t)opaque;
 +
-+    if (verbose < type)
-+        return;
++	if (verbose < type)
++		return;
++	if (asprintf(&new_format, "[vlc.js: 0x%"PRIxPTR"/0x%"PRIxPTR"] %s %s: %s",
++				 p_item->i_object_id, p_item->tid, p_item->psz_module,
++				 p_item->psz_object_type, format) < 0)
++		return;
++	
++	switch (type) {
++		case VLC_MSG_ERR:
++			prio = EM_LOG_ERROR;
++			break;
++		case VLC_MSG_WARN:
++			prio = EM_LOG_WARN;
++			break;
++		default:
++		case VLC_MSG_DBG:
++			prio = EM_LOG_CONSOLE;
++	}
 +
-+    if (asprintf(&format2, "[LIBVLC DEBUG] %s %s: %s", p_item->psz_module, p_item->psz_object_type, format) < 0)
-+		 return;
-+    switch (type) {
-+        case VLC_MSG_ERR:
-+            prio = EM_LOG_ERROR;
-+            break;
-+        case VLC_MSG_WARN:
-+            prio = EM_LOG_WARN;
-+            break;
-+        default:
-+        case VLC_MSG_DBG:
-+            prio = EM_LOG_CONSOLE;
-+    }
-+    emscripten_log(prio, format2, ap);
-+    free(format2);
++	if (vasprintf(&message, new_format, ap) < 0)
++	{
++		free(new_format);
++		return ;
++	}
++	free(new_format);
++	emscripten_log(prio, "%s", message);
++	free(message);
 +}
 +
 +static const struct vlc_logger_operations ops = { EmscriptenPrintMsg, NULL };
 +
 +static const struct vlc_logger_operations *Open(vlc_object_t *obj, void **sysp)
 +{
-+    int verbosity = var_InheritInteger(obj, "verbose");
++	int verbosity = var_InheritInteger(obj, "verbose");
 +
-+    if (verbosity < 0)
-+        return NULL;
++	if (verbosity < 0)
++		return NULL;
 +
-+    verbosity += VLC_MSG_ERR;
-+    *sysp = (void *)(uintptr_t)verbosity;
++	verbosity += VLC_MSG_ERR;
++	*sysp = (void *)(uintptr_t)verbosity;
 +
-+    return &ops;
++	return &ops;
 +}
 +
 +vlc_module_begin()
-+    set_shortname(N_("Emscripten log"))
-+    set_description(N_("Emscripten log using logcat"))
-+    set_category(CAT_ADVANCED)
-+    set_subcategory(SUBCAT_ADVANCED_MISC)
-+    set_capability("logger", 30)
-+    set_callbacks(Open, NULL)
++	set_shortname(N_("emlog"))
++	set_description(N_("Emscripten loggger"))
++	set_category(CAT_ADVANCED)
++	set_subcategory(SUBCAT_ADVANCED_MISC)
++	set_capability("logger", 30)
++	set_callbacks(Open, NULL)
 +vlc_module_end ()
 -- 
-2.23.0
+2.30.2
 
diff --git a/vlc_patches/nacl-wasm/0001-contrib-ass-add-support-for-wasm-emscripten.patch b/vlc_patches/nacl-wasm/0001-contrib-ass-add-support-for-wasm-emscripten.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f7c023b3eaff909e570a3484d4d7cbdf7b101cf6
--- /dev/null
+++ b/vlc_patches/nacl-wasm/0001-contrib-ass-add-support-for-wasm-emscripten.patch
@@ -0,0 +1,172 @@
+From fd1e539e2a585fb69ede782bd0180579e853597f Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Sun, 30 May 2021 19:22:31 +0200
+Subject: [PATCH 1/5] contrib: ass: add support for wasm-emscripten
+
+---
+ contrib/src/ass/rules.mak                     |   5 +
+ ...nitial-support-for-wasm32-emscripten.patch | 105 ++++++++++++++++++
+ contrib/src/fontconfig/rules.mak              |   4 +
+ contrib/src/harfbuzz/rules.mak                |   3 +-
+ 4 files changed, 116 insertions(+), 1 deletion(-)
+ create mode 100644 contrib/src/fontconfig/add-initial-support-for-wasm32-emscripten.patch
+
+diff --git a/contrib/src/ass/rules.mak b/contrib/src/ass/rules.mak
+index fc6a90bb03..d84b209e77 100644
+--- a/contrib/src/ass/rules.mak
++++ b/contrib/src/ass/rules.mak
+@@ -25,6 +25,11 @@ WITH_DWRITE = 1
+ else
+ WITH_FONTCONFIG = 1
+ WITH_HARFBUZZ = 1
++ifdef HAVE_EMSCRIPTEN
++WITH_FONTCONFIG = 1
++WITH_HARFBUZZ = 1
++WITH_ASS_ASM = 0
++endif
+ endif
+ endif
+ endif
+diff --git a/contrib/src/fontconfig/add-initial-support-for-wasm32-emscripten.patch b/contrib/src/fontconfig/add-initial-support-for-wasm32-emscripten.patch
+new file mode 100644
+index 0000000000..b1308fb19e
+--- /dev/null
++++ b/contrib/src/fontconfig/add-initial-support-for-wasm32-emscripten.patch
+@@ -0,0 +1,105 @@
++From b7f21ca85efd78c8034223c63786a0c01b8378fe Mon Sep 17 00:00:00 2001
++From: Mehdi Sabwat <mehdi@videolabs.io>
++Date: Wed, 9 Jun 2021 03:42:51 +0200
++Subject: [PATCH 1/1] add initial support for wasm32-emscripten
++
++This commit adds a check for uuid_generate_random which is not supported for now, and fixes a failing test.
++It also handles a case where F_FSTYPENAME field is not present in statfs struct.
++---
++ configure.ac               | 1 +
++ src/Makefile.am            | 1 +
++ src/fcint.h                | 5 +++++
++ src/fcstat.c               | 2 +-
++ src/uuid_generate_random.c | 9 +++++++++
++ test/test-hash.c           | 5 ++++-
++ 6 files changed, 21 insertions(+), 2 deletions(-)
++ create mode 100644 src/uuid_generate_random.c
++
++diff --git a/configure.ac b/configure.ac
++index fb8af46..018cfc1 100644
++--- a/configure.ac
+++++ b/configure.ac
++@@ -171,2 +171,3 @@ AC_FUNC_VPRINTF
++ AC_FUNC_MMAP
++ AC_CHECK_FUNCS([link mkstemp mkostemp _mktemp_s mkdtemp getopt getopt_long getprogname getexecname rand random lrand48 random_r rand_r readlink fstatvfs fstatfs lstat strerror strerror_r]) 
+++AC_REPLACE_FUNCS([uuid_generate_random])
++
++ dnl AC_CHECK_FUNCS doesn't check for header files.
++ dnl posix_fadvise() may be not available in older libc.
++ AC_CHECK_SYMBOL([posix_fadvise], [fcntl.h], [fc_func_posix_fadvise=1], [fc_func_posix_fadvise=0])
++diff --git a/src/Makefile.am b/src/Makefile.am
++index 7b414df..de1d785 100644
++--- a/src/Makefile.am
+++++ b/src/Makefile.am
++@@ -127,6 +127,7 @@ EXTRA_DIST += \
++ 	fcobjshash.gperf.h
++ 
++ libfontconfig_la_SOURCES = \
+++	uuid_generate_random.c \
++ 	fcarch.h \
++ 	fcatomic.c \
++ 	fcatomic.h \
++diff --git a/src/fcint.h b/src/fcint.h
++index a9d075a..d8fdbfd 100644
++--- a/src/fcint.h
+++++ b/src/fcint.h
++@@ -598,6 +598,11 @@ struct _FcValuePromotionBuffer {
++ FcPrivate FcCache *
++ FcDirCacheScan (const FcChar8 *dir, FcConfig *config);
++ 
+++#ifndef HAVE_UUID_GENERATE_RANDOM
+++#include <uuid/uuid.h>
+++void uuid_generate_random(uuid_t out);
+++#endif
+++
++ FcPrivate FcCache *
++ FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcStrSet *dirs);
++ 
++diff --git a/src/fcstat.c b/src/fcstat.c
++index 5aa1643..d1240c5 100644
++--- a/src/fcstat.c
+++++ b/src/fcstat.c
++@@ -384,7 +384,7 @@ FcFStatFs (int fd, FcStatFS *statb)
++ #  endif
++ #  if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
++ 	p = buf.f_fstypename;
++-#  elif defined(__linux__)
+++#  elif defined(__linux__) || defined(__EMSCRIPTEN__)
++ 	switch (buf.f_type)
++ 	{
++ 	case 0x6969: /* nfs */
++diff --git a/src/uuid_generate_random.c b/src/uuid_generate_random.c
++new file mode 100644
++index 0000000..c17a58d
++--- /dev/null
+++++ b/src/uuid_generate_random.c
++@@ -0,0 +1,9 @@
+++// compat function for uuid_generate_random
+++#include "fcint.h"
+++
+++#ifndef HAVE_UUID_GENERATE_RANDOM
+++void uuid_generate_random(uuid_t out)
+++{
+++    uuid_generate(out);
+++}
+++#endif
++diff --git a/test/test-hash.c b/test/test-hash.c
++index 7530e82..221029d 100644
++--- a/test/test-hash.c
+++++ b/test/test-hash.c
++@@ -51,8 +51,11 @@ test_add (Test *test, FcChar8 *key, FcBool replace)
++     void *u;
++     FcBool (*hash_add) (FcHashTable *, void *, void *);
++     FcBool ret = FcFalse;
++-
+++#ifdef HAVE_UUID_GENERATE_RANDOM
++     uuid_generate_random (uuid);
+++#else
+++    uuid_generate(uuid);
+++#endif
++     if (replace)
++ 	hash_add = FcHashTableReplace;
++     else
++-- 
++2.32.0
++
+diff --git a/contrib/src/fontconfig/rules.mak b/contrib/src/fontconfig/rules.mak
+index b761d44bd5..d187d211ae 100644
+--- a/contrib/src/fontconfig/rules.mak
++++ b/contrib/src/fontconfig/rules.mak
+@@ -23,6 +23,10 @@ ifdef HAVE_WIN32
+ endif
+ 	$(APPLY) $(SRC)/fontconfig/8208f99-fix-static-linking.patch
+ 	$(call pkg_static, "fontconfig.pc.in")
++ifdef HAVE_EMSCRIPTEN
++	$(APPLY) $(SRC)/fontconfig/add-initial-support-for-wasm32-emscripten.patch
++	$(UPDATE_AUTOCONFIG)
++endif
+ 	$(MOVE)
+ 
+ FONTCONFIG_CONF := $(HOSTCONF) \
+diff --git a/contrib/src/harfbuzz/rules.mak b/contrib/src/harfbuzz/rules.mak
+index 1ea81242e3..c1bc7fa5c3 100644
+--- a/contrib/src/harfbuzz/rules.mak
++++ b/contrib/src/harfbuzz/rules.mak
+@@ -29,7 +29,8 @@ HARFBUZZ_CONF += --with-coretext
+ endif
+ 
+ .harfbuzz: harfbuzz
+-	$(RECONF)
++	# https://savannah.gnu.org/support/index.php?110503
++	export GTKDOCIZE=true && $(RECONF)
+ 	cd $< && $(HOSTVARS_PIC) ./configure $(HOSTCONF) $(HARFBUZZ_CONF) ICU_CONFIG=false
+ 	cd $< && $(MAKE) install
+ 	touch $@
+-- 
+2.30.2
+
diff --git a/vlc_patches/nacl-wasm/0002-contrib-gcrypt-add-support-for-wasm-emscripten.patch b/vlc_patches/nacl-wasm/0002-contrib-gcrypt-add-support-for-wasm-emscripten.patch
new file mode 100644
index 0000000000000000000000000000000000000000..84ab61769bc5292fb1de35ef2e84840385bbcd28
--- /dev/null
+++ b/vlc_patches/nacl-wasm/0002-contrib-gcrypt-add-support-for-wasm-emscripten.patch
@@ -0,0 +1,93 @@
+From ca88c8936b94312f6bfe844c9dacb310d7fee957 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Sun, 30 May 2021 20:38:46 +0200
+Subject: [PATCH 2/5] contrib: gcrypt: add support for wasm-emscripten
+
+---
+ contrib/src/gcrypt/rules.mak           |  6 ++++
+ contrib/src/gpg-error/emscripten.patch | 43 ++++++++++++++++++++++++++
+ contrib/src/gpg-error/rules.mak        |  1 +
+ 3 files changed, 50 insertions(+)
+ create mode 100644 contrib/src/gpg-error/emscripten.patch
+
+diff --git a/contrib/src/gcrypt/rules.mak b/contrib/src/gcrypt/rules.mak
+index c9c9be3efa..0275e21fe8 100644
+--- a/contrib/src/gcrypt/rules.mak
++++ b/contrib/src/gcrypt/rules.mak
+@@ -72,6 +72,12 @@ ifeq ($(ARCH),aarch64)
+ GCRYPT_CONF += --disable-arm-crypto-support
+ endif
+ endif
++ifdef HAVE_EMSCRIPTEN
++GCRYPT_CONF += --disable-asm --disable-aesni-support ac_cv_func_syslog=no --disable-sse41-support
++GCRYPT_CONF += --disable-avx-support --disable-avx2-support --disable-padlock-support
++GCRYPT_CONF += --disable-amd64-as-feature-detection --disable-drng-support
++GCRYPT_CONF += --disable-pclmul-support
++endif
+ 
+ .gcrypt: gcrypt
+ 	# Reconfiguring this requires a git repo to be available, to
+diff --git a/contrib/src/gpg-error/emscripten.patch b/contrib/src/gpg-error/emscripten.patch
+new file mode 100644
+index 0000000000..f60695c513
+--- /dev/null
++++ b/contrib/src/gpg-error/emscripten.patch
+@@ -0,0 +1,43 @@
++From 63aa1523659914acd6c84229fb31ff9b712fbf8b Mon Sep 17 00:00:00 2001
++From: Mehdi Sabwat <mehdi@videolabs.io>
++Date: Wed, 2 Jun 2021 11:42:46 +0200
++Subject: [PATCH 1/1] emscripten
++
++---
++ .../lock-obj-pub.wasm32-unknown-emscripten.h  | 24 +++++++++++++++++++
++ 1 file changed, 24 insertions(+)
++ create mode 100644 src/syscfg/lock-obj-pub.wasm32-unknown-emscripten.h
++
++diff --git a/src/syscfg/lock-obj-pub.wasm32-unknown-emscripten.h b/src/syscfg/lock-obj-pub.wasm32-unknown-emscripten.h
++new file mode 100644
++index 0000000..1651518
++--- /dev/null
+++++ b/src/syscfg/lock-obj-pub.wasm32-unknown-emscripten.h
++@@ -0,0 +1,24 @@
+++## lock-obj-pub.wasm32-unknown-emscripten.h
+++## File created by gen-posix-lock-obj - DO NOT EDIT
+++## To be included by mkheader into gpg-error.h
+++
+++typedef struct
+++{
+++  long _vers;
+++  union {
+++    volatile char _priv[28];
+++    long _x_align;
+++    long *_xp_align;
+++  } u;
+++} gpgrt_lock_t;
+++
+++#define GPGRT_LOCK_INITIALIZER {1,{{0,0,0,0,0,0,0,0, \
+++                                    0,0,0,0,0,0,0,0, \
+++                                    0,0,0,0,0,0,0,0, \
+++                                    0,0,0,0}}}
+++##
+++## Local Variables:
+++## mode: c
+++## buffer-read-only: t
+++## End:
+++##
++-- 
++2.31.1
++
+diff --git a/contrib/src/gpg-error/rules.mak b/contrib/src/gpg-error/rules.mak
+index 4e283c77cb..0f1053587d 100644
+--- a/contrib/src/gpg-error/rules.mak
++++ b/contrib/src/gpg-error/rules.mak
+@@ -25,6 +25,7 @@ endif
+ 	$(APPLY) $(SRC)/gpg-error/version-bump-gawk-5.patch
+ 	$(APPLY) $(SRC)/gpg-error/win32-extern-struct.patch
+ 	$(APPLY) $(SRC)/gpg-error/darwin-triplet.patch
++	$(APPLY) $(SRC)/gpg-error/emscripten.patch
+ 	$(MOVE)
+ ifdef HAVE_ANDROID
+ ifeq ($(ARCH),aarch64)
+-- 
+2.30.2
+
diff --git a/vlc_patches/nacl-wasm/0003-contrib-gmp-add-support-for-wasm-emscripten.patch b/vlc_patches/nacl-wasm/0003-contrib-gmp-add-support-for-wasm-emscripten.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3fce285641d905911fee629611b29ff495386757
--- /dev/null
+++ b/vlc_patches/nacl-wasm/0003-contrib-gmp-add-support-for-wasm-emscripten.patch
@@ -0,0 +1,26 @@
+From cc28db23a9fc0fc2b116c33a26d72abf2dad7a4f Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Sun, 30 May 2021 21:22:40 +0200
+Subject: [PATCH 3/5] contrib: gmp: add support for wasm-emscripten
+
+---
+ contrib/src/gmp/rules.mak | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/contrib/src/gmp/rules.mak b/contrib/src/gmp/rules.mak
+index 11fe8acdfa..d6cd702ce0 100644
+--- a/contrib/src/gmp/rules.mak
++++ b/contrib/src/gmp/rules.mak
+@@ -13,6 +13,9 @@ ifeq ($(ARCH),mips64el)
+ GMP_CONF += --disable-assembly
+ endif
+ endif
++ifdef HAVE_EMSCRIPTEN
++GMP_CONF += --disable-assembly
++endif
+ 
+ ifdef HAVE_WIN32
+ ifeq ($(ARCH),arm)
+-- 
+2.30.2
+
diff --git a/vlc_patches/nacl-wasm/0004-contrib-postproc-add-support-for-wasm-emscripten.patch b/vlc_patches/nacl-wasm/0004-contrib-postproc-add-support-for-wasm-emscripten.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9d7e904a29fce36aba2e625662c2c83941d4963b
--- /dev/null
+++ b/vlc_patches/nacl-wasm/0004-contrib-postproc-add-support-for-wasm-emscripten.patch
@@ -0,0 +1,27 @@
+From 1e1459946fce7442ec6ca942d27227c8b19ff9ef Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Sun, 30 May 2021 22:00:12 +0200
+Subject: [PATCH 4/5] contrib: postproc: add support for wasm-emscripten
+
+---
+ contrib/src/postproc/rules.mak | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/contrib/src/postproc/rules.mak b/contrib/src/postproc/rules.mak
+index 252d6b384d..8a008f6ad2 100644
+--- a/contrib/src/postproc/rules.mak
++++ b/contrib/src/postproc/rules.mak
+@@ -103,6 +103,10 @@ ifdef HAVE_SOLARIS
+ POSTPROCCONF += --enable-pic
+ endif
+ 
++ifdef HAVE_EMSCRIPTEN
++POSTPROCCONF += --arch=wasm32 --target-os=linux
++endif
++
+ # Build
+ 
+ ifdef GPL
+-- 
+2.30.2
+
diff --git a/vlc_patches/nacl-wasm/0005-contrib-gnutls-add-support-for-wasm-emscripten.patch b/vlc_patches/nacl-wasm/0005-contrib-gnutls-add-support-for-wasm-emscripten.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bc353a855cd7e064b311fb92a0ec4ff4a1fba247
--- /dev/null
+++ b/vlc_patches/nacl-wasm/0005-contrib-gnutls-add-support-for-wasm-emscripten.patch
@@ -0,0 +1,29 @@
+From 1627322eb7b9a485ce5380978e4db1fd5b331ee5 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Mon, 31 May 2021 00:18:18 +0200
+Subject: [PATCH 5/5] contrib: gnutls: add support for wasm-emscripten
+
+the contrib is temporarily disabled until this pr is merged:
+https://github.com/emscripten-core/emscripten/pull/14352
+---
+ contrib/src/gnutls/rules.mak | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/contrib/src/gnutls/rules.mak b/contrib/src/gnutls/rules.mak
+index a5431693c1..012384d65b 100644
+--- a/contrib/src/gnutls/rules.mak
++++ b/contrib/src/gnutls/rules.mak
+@@ -78,6 +78,10 @@ ifeq ($(ARCH),aarch64)
+ endif
+ endif
+ 
++ifdef HAVE_EMSCRIPTEN
++	GNUTLS_CONF += --disable-hardware-acceleration
++endif
++
+ .gnutls: gnutls
+ 	cd $< && $(GNUTLS_ENV) ./configure $(GNUTLS_CONF)
+ 	cd $< && $(MAKE) -C gl install
+-- 
+2.30.2
+
diff --git a/vlc_patches/openal/0001-Add-an-openal-audio-module.patch b/vlc_patches/openal/0001-Add-an-openal-audio-module.patch
deleted file mode 100644
index ceeb4837de856fc03d372bb13d837abfcd03045f..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0001-Add-an-openal-audio-module.patch
+++ /dev/null
@@ -1,344 +0,0 @@
-From b1cefb4947b07ce2e4064c03d6cc20c3e6013af5 Mon Sep 17 00:00:00 2001
-From: Etienne Brateau <etienne.brateau@gmail.com>
-Date: Tue, 29 Aug 2017 13:30:51 +0200
-Subject: [PATCH 1/6] Add an openal audio module.
-
----
- modules/audio_output/Makefile.am |  11 ++
- modules/audio_output/openal.c    | 305 +++++++++++++++++++++++++++++++
- 2 files changed, 316 insertions(+)
- create mode 100644 modules/audio_output/openal.c
-
-diff --git a/modules/audio_output/Makefile.am b/modules/audio_output/Makefile.am
-index 4af4d78c5f..120673ca6a 100644
---- a/modules/audio_output/Makefile.am
-+++ b/modules/audio_output/Makefile.am
-@@ -117,3 +117,14 @@ endif
- if HAVE_TVOS
- aout_LTLIBRARIES += libaudiounit_ios_plugin.la
- endif
-+
-+libtizen_audio_plugin_la_SOURCES = audio_output/tizen_audio.c
-+libtizen_audio_plugin_la_CFLAGS = $(AM_CFLAGS)
-+EXTRA_LTLIBRARIES += libtizen_audio_plugin.la
-+aout_LTLIBRARIES += $(LTLIBtizen_audio)
-+
-+libopenal_audio_plugin_la_SOURCES = audio_output/openal.c
-+libopenal_audio_plugin_la_CFLAGS = $(AM_CFLAGS)
-+libopenal_audio_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)'
-+libopenal_audio_plugin_la_LIBADD = -lopenal
-+aout_LTLIBRARIES += libopenal_audio_plugin.la
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-new file mode 100644
-index 0000000000..1c0b97a358
---- /dev/null
-+++ b/modules/audio_output/openal.c
-@@ -0,0 +1,305 @@
-+/*****************************************************************************
-+ * openal.c: output module to allow OpenAL integration
-+ *****************************************************************************
-+ *
-+ * Authors: Shaurav Garg <shauravg@gmail.com>
-+ *          Etienne Brateau <etienne.brateau@gmail.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software Foundation, Inc.,
-+ * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
-+ *****************************************************************************/
-+
-+/*****************************************************************************
-+ * Preamble
-+ *****************************************************************************/
-+
-+#ifdef HAVE_CONFIG_H
-+# include "config.h"
-+#endif
-+
-+#include <assert.h>
-+#include <vlc_common.h>
-+#include <vlc_plugin.h>
-+#include <vlc_aout.h>
-+
-+#include <AL/al.h>
-+#include <AL/alc.h>
-+#ifndef __EMSCRIPTEN__
-+#include <AL/alext.h>
-+#endif
-+
-+#if defined __EMSCRIPTEN__
-+#include <emscripten.h>
-+/* This format are supported in openAL by emscripten but the define are not done */
-+#define AL_FORMAT_MONO_FLOAT32					 0x10010
-+#define AL_FORMAT_STEREO_FLOAT32                 0x10011
-+#endif
-+
-+/*****************************************************************************
-+ * Local prototypes.
-+ *****************************************************************************/
-+static int  Open 		( vlc_object_t * );
-+static void Close		( vlc_object_t * );
-+static void Stop		( audio_output_t *);
-+static void Play      	( audio_output_t *, block_t * );
-+static void Flush		( audio_output_t *, bool );
-+static void Pause		( audio_output_t *, bool , mtime_t );
-+static int Time_Get		( audio_output_t *, mtime_t * );
-+static int Start		( audio_output_t *, audio_sample_format_t *restrict );
-+static int DeviceSelect	( audio_output_t *, const char *);
-+//static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
-+//                                vlc_value_t newval, vlc_value_t oldval, void *p_unused );
-+//static void GetDevices( vlc_object_t *, module_config_t * );
-+
-+/*****************************************************************************
-+ * Used to configure OpenAL
-+ ****************************************************************************/
-+#define NUM_BUF 255
-+#define CHUNK_SIZE 512
-+#define BUFFER_SIZE 4096
-+#define ALC_ALL_DEVICES_SPECIFIER 0x1013
-+
-+/*static const char *const ppsz_devices[] = {
-+    "default", "default",};
-+
-+static const char *const ppsz_devices_text[] = {
-+    N_("Default"), N_("Default"),};
-+*/
-+static ALenum openalFormat = AL_FORMAT_STEREO_FLOAT32;
-+
-+/*****************************************************************************
-+ * aout_sys_t: openal audio output method descriptor
-+ *****************************************************************************
-+ * This structure is part of the audio output thread descriptor.
-+ * It describes the direct sound specific properties of an audio device.
-+ *****************************************************************************/
-+struct aout_sys_t
-+{
-+	ALCdevice *dev;
-+	ALCcontext *ctx;
-+	ALsizei freq;
-+	mtime_t start_date;
-+	ALuint buffers[NUM_BUF];
-+	ALuint source;
-+	ALuint is_playing;
-+	ALuint next_buffer;
-+};
-+
-+/*****************************************************************************
-+ * Module descriptor
-+ *****************************************************************************/
-+#define DEVICE_TEXT N_("Output device")
-+#define DEVICE_LONGTEXT N_("Select your audio output device")
-+
-+vlc_module_begin ()
-+    set_description( N_("OpenAL audio output") )
-+    set_shortname( "OpenAL" )
-+    set_capability( "audio output", 100 )
-+    set_category( CAT_AUDIO )
-+    set_subcategory( SUBCAT_AUDIO_AOUT )
-+//        change_string_list( ppsz_devices, ppsz_devices_text )
-+//		change_string_cb( FindDevicesCallback )
-+//       change_action_add( FindDevicesCallback, N_("Refresh list") )
-+
-+    add_shortcut( "openal", "openal" )
-+
-+    add_string( "openal-audio-device-name", "default",
-+             DEVICE_TEXT, DEVICE_LONGTEXT, false )
-+
-+    set_callbacks( Open, Close )
-+vlc_module_end ()
-+
-+/*****************************************************************************
-+ * Open: open the audio device
-+ *****************************************************************************
-+ * This function opens and setups Direct Sound.
-+ *****************************************************************************/
-+static int Open( vlc_object_t *obj )
-+{
-+	audio_output_t * aout = (audio_output_t *) obj;
-+
-+	/* Allocate structures */
-+	aout_sys_t *sys = malloc( sizeof( *sys ) );
-+	if( unlikely(sys == NULL) )
-+        	return VLC_ENOMEM;
-+
-+
-+	aout->sys = sys;
-+	aout->start = Start;
-+	aout->stop = Stop;
-+	//aout_SoftVolumeInit(aout);
-+	aout->play = Play;
-+	aout->pause = Pause;
-+	aout->flush = Flush;
-+	//aout->time_get = Time_Get;
-+	aout->pause = Pause;
-+	aout->volume_set = NULL;
-+	aout->mute_set = NULL;
-+//	aout->device_select = DeviceSelect;
-+
-+	return VLC_SUCCESS;
-+}
-+
-+static int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
-+{
-+	aout_sys_t *sys = aout->sys;
-+	
-+	if ( aout_FormatNbChannels(fmt) == 0 )
-+		return VLC_EGENERIC;
-+
-+	fmt->i_format = VLC_CODEC_FL32;
-+
-+	float pos[3] = {0,0,0};
-+	float vel[3] = {0,0,0};
-+	float dir[6] = {0,0,-1,0,1,0};
-+	float speakerPos[3] = {0,0,1};
-+	//ALCint attrs[] = {ALC_FREQUENCY, fmt->i_rate, 0, 0};
-+
-+	const ALCchar* devInputName = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); 
-+	sys->dev = alcOpenDevice(devInputName);
-+	if(!sys->dev)
-+	{
-+		msg_Err( aout, "cannot open OpenAL Device : %s", devInputName );
-+		return VLC_ENOMEM;
-+	}
-+	sys->ctx = alcCreateContext(sys->dev, NULL);
-+	if(!sys->dev)
-+	{
-+		msg_Err( aout, "cannot create OpenAL Context" );
-+		return VLC_ENOMEM;
-+	}
-+	alcMakeContextCurrent(sys->ctx);
-+
-+	alListenerfv(AL_POSITION, pos);
-+	alListenerfv(AL_VELOCITY, vel);
-+	alListenerfv(AL_ORIENTATION, dir);
-+	alGenSources(1, &sys->source);
-+
-+	alGenBuffers(NUM_BUF, &sys->buffers);
-+	alSourcefv(sys->source, AL_POSITION, speakerPos);
-+	alSource3f(sys->source, AL_VELOCITY, 0, 0, 0);
-+
-+	alcGetIntegerv(sys->dev, ALC_FREQUENCY, 1, &sys->freq);
-+	msg_Dbg(aout, "Openal Dev freq: %d", sys->freq);
-+
-+	sys->start_date = 0;
-+	sys->next_buffer = 0;
-+
-+	return VLC_SUCCESS;
-+}
-+
-+static void Stop (audio_output_t *aout)
-+{
-+	aout_sys_t *sys = aout->sys;
-+
-+	alSourcei(sys->source, AL_BUFFER, 0);
-+	alDeleteBuffers(NUM_BUF, &sys->buffers);
-+	alDeleteSources(1, &sys->source);
-+
-+	alcDestroyContext(sys->ctx);
-+	alcCloseDevice(sys->dev);
-+}
-+
-+/*****************************************************************************
-+ * Play: method description here
-+ *****************************************************************************/
-+static void Play( audio_output_t *aout, block_t *block )
-+{
-+	aout_sys_t *sys = aout->sys;
-+	const void* data = block->p_buffer;
-+	size_t datalen = block->i_buffer;
-+
-+
-+	ALenum err;
-+	ALint buffersProcessed = 0;
-+
-+	alGetSourcei(sys->source, AL_BUFFERS_PROCESSED, &buffersProcessed);
-+	//msg_Dbg(aout, "buffered processed / next buffer : %i / %i", buffersProcessed, sys->next_buffer);
-+	ALint tmpBuffers[buffersProcessed];
-+
-+	alSourceUnqueueBuffers(sys->source, buffersProcessed, &tmpBuffers);
-+
-+	//fill with silence originally
-+	if(buffersProcessed > 0)
-+		alBufferData(tmpBuffers[0], openalFormat, data, datalen, sys->freq);
-+	else
-+	{
-+		alBufferData(sys->buffers[sys->next_buffer], openalFormat, data, datalen, sys->freq);
-+	}
-+
-+	if(buffersProcessed > 0)
-+	{
-+		alSourceQueueBuffers(sys->source, 1, &tmpBuffers[0]);
-+	}
-+	else
-+	{
-+		alSourceQueueBuffers(sys->source, 1, &sys->buffers[sys->next_buffer]);
-+		sys->next_buffer = (sys->next_buffer + 1) % NUM_BUF;
-+	}
-+	
-+	alGetSourcei(sys->source, AL_SOURCE_STATE, &sys->is_playing);
-+	if(sys->is_playing != AL_PLAYING)
-+		alSourcePlay(sys->source);
-+	
-+	block_Release(block);
-+}
-+
-+static void Pause( audio_output_t *aout, bool paused, mtime_t date )
-+{
-+	aout_sys_t *sys = aout->sys;
-+	// TODO
-+}
-+
-+static void Flush( audio_output_t *aout, bool wait )
-+{
-+	aout_sys_t * sys = aout->sys;
-+	// TODO
-+}
-+
-+static int DeviceSelect( audio_output_t *aout, const char *psz_device)
-+{
-+	/*if ( psz_device == NULL )
-+		return VLC_EGENERIC;
-+	char* psz_end;
-+	intptr_t ptr = strtoll( psz_device, &psz_end, 16 );
-+	if( *psz_end != 0 )
-+		return VLC_EGENERIC;
-+	if (aout->sys->dev == (ALCdevice*)ptr)
-+		return VLC_SUCCESS;*/
-+	const ALCchar* devInputName = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); 
-+	aout->sys->dev = alcOpenDevice(devInputName);
-+	var_SetAddress (aout->obj.parent, "emscripten-openal-device", aout->sys->dev );
-+	aout_RestartRequest( aout, AOUT_RESTART_OUTPUT );
-+	return VLC_SUCCESS;
-+}
-+
-+/*static void Time_Get( audio_output_t *aout, mtime_t *delay)
-+{
-+	aout_sys_t *sys = aout->sys;
-+
-+	return 0;
-+}*/
-+
-+/*****************************************************************************
-+ * CloseAudio: close the audio device
-+ *****************************************************************************/
-+static void Close( vlc_object_t *obj )
-+{
-+	msg_Dbg(obj, "Openal Close");
-+	audio_output_t *aout = (audio_output_t *)obj;
-+	struct aout_sys_t *sys = aout->sys;
-+
-+	free(sys);
-+}
-+
--- 
-2.23.0
-
diff --git a/vlc_patches/openal/0002-wip-adapt-code-to-4.0-api.patch b/vlc_patches/openal/0002-wip-adapt-code-to-4.0-api.patch
deleted file mode 100644
index cf98e1c45466d95b8c59cc71cfc1557c26c3cc7e..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0002-wip-adapt-code-to-4.0-api.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 9b46de215341f5ad54f2215c4bc41fa8e40cc1e9 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Tue, 1 Oct 2019 19:34:46 +0200
-Subject: [PATCH 2/6] wip: adapt code to 4.0 api
-
----
- modules/audio_output/openal.c | 10 ++++++----
- 1 file changed, 6 insertions(+), 4 deletions(-)
-
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-index 1c0b97a358..6d4a707642 100644
---- a/modules/audio_output/openal.c
-+++ b/modules/audio_output/openal.c
-@@ -84,7 +84,7 @@ static ALenum openalFormat = AL_FORMAT_STEREO_FLOAT32;
-  * This structure is part of the audio output thread descriptor.
-  * It describes the direct sound specific properties of an audio device.
-  *****************************************************************************/
--struct aout_sys_t
-+typedef struct aout_sys_t
- {
- 	ALCdevice *dev;
- 	ALCcontext *ctx;
-@@ -94,7 +94,7 @@ struct aout_sys_t
- 	ALuint source;
- 	ALuint is_playing;
- 	ALuint next_buffer;
--};
-+} aout_sys_t;
- 
- /*****************************************************************************
-  * Module descriptor
-@@ -269,6 +269,7 @@ static void Flush( audio_output_t *aout, bool wait )
- 
- static int DeviceSelect( audio_output_t *aout, const char *psz_device)
- {
-+  aout_sys_t *sys = aout->sys;
- 	/*if ( psz_device == NULL )
- 		return VLC_EGENERIC;
- 	char* psz_end;
-@@ -278,8 +279,9 @@ static int DeviceSelect( audio_output_t *aout, const char *psz_device)
- 	if (aout->sys->dev == (ALCdevice*)ptr)
- 		return VLC_SUCCESS;*/
- 	const ALCchar* devInputName = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); 
--	aout->sys->dev = alcOpenDevice(devInputName);
--	var_SetAddress (aout->obj.parent, "emscripten-openal-device", aout->sys->dev );
-+
-+	sys->dev = alcOpenDevice(devInputName);
-+	var_SetAddress (vlc_object_parent(aout), "emscripten-openal-device", sys->dev );
- 	aout_RestartRequest( aout, AOUT_RESTART_OUTPUT );
- 	return VLC_SUCCESS;
- }
--- 
-2.23.0
-
diff --git a/vlc_patches/openal/0003-aout-add-Timeg_Get-stub-for-OpenAl.patch b/vlc_patches/openal/0003-aout-add-Timeg_Get-stub-for-OpenAl.patch
deleted file mode 100644
index 34c622da7b4af37733a95cda355a0b6c9f098f84..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0003-aout-add-Timeg_Get-stub-for-OpenAl.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 7504f01518164b091e5b0cf8c5e0dc9679cb1138 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Wed, 2 Oct 2019 22:05:01 +0200
-Subject: [PATCH 3/6] aout: add Timeg_Get stub for OpenAl
-
-to avoid aborting in src/audio_output/output.c
----
- modules/audio_output/openal.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-index 6d4a707642..c48c5a0f5e 100644
---- a/modules/audio_output/openal.c
-+++ b/modules/audio_output/openal.c
-@@ -142,7 +142,7 @@ static int Open( vlc_object_t *obj )
- 	aout->play = Play;
- 	aout->pause = Pause;
- 	aout->flush = Flush;
--	//aout->time_get = Time_Get;
-+	aout->time_get = Time_Get;
- 	aout->pause = Pause;
- 	aout->volume_set = NULL;
- 	aout->mute_set = NULL;
-@@ -286,12 +286,12 @@ static int DeviceSelect( audio_output_t *aout, const char *psz_device)
- 	return VLC_SUCCESS;
- }
- 
--/*static void Time_Get( audio_output_t *aout, mtime_t *delay)
-+static int Time_Get( audio_output_t *aout, mtime_t *delay)
- {
- 	aout_sys_t *sys = aout->sys;
--
-+	*delay = aout_TimeGetDefault(aout, delay);
- 	return 0;
--}*/
-+}
- 
- /*****************************************************************************
-  * CloseAudio: close the audio device
--- 
-2.23.0
-
diff --git a/vlc_patches/openal/0004-wip-delete-call-to-msg_Dbg.patch b/vlc_patches/openal/0004-wip-delete-call-to-msg_Dbg.patch
deleted file mode 100644
index 9c4a476c68dccf6ab9e14fa3884330687909b6f7..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0004-wip-delete-call-to-msg_Dbg.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 0aa4efe1deb2fe6bfcb51be19e876f85738078e2 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 3 Oct 2019 22:32:12 +0200
-Subject: [PATCH 4/6] wip: delete call to msg_Dbg
-
-The logger module needs to be fixed, there is something wrong with it.
-some calls to msg_Dbg on various places
-(audio output/audio filters/MP4 demux/access, keystore, and maybe more...)
-triggers Runtime Errors (out of bounds).
-Also, we see garbage values in the console instead of actual output.
----
- modules/audio_output/openal.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-index c48c5a0f5e..2a5f6f946d 100644
---- a/modules/audio_output/openal.c
-+++ b/modules/audio_output/openal.c
-@@ -191,7 +191,8 @@ static int Start( audio_output_t *aout, audio_sample_format_t *restrict fmt )
- 	alSource3f(sys->source, AL_VELOCITY, 0, 0, 0);
- 
- 	alcGetIntegerv(sys->dev, ALC_FREQUENCY, 1, &sys->freq);
--	msg_Dbg(aout, "Openal Dev freq: %d", sys->freq);
-+	
-+	//msg_Dbg(aout, "Openal Dev freq: %d", sys->freq);
- 
- 	sys->start_date = 0;
- 	sys->next_buffer = 0;
-@@ -298,7 +299,7 @@ static int Time_Get( audio_output_t *aout, mtime_t *delay)
-  *****************************************************************************/
- static void Close( vlc_object_t *obj )
- {
--	msg_Dbg(obj, "Openal Close");
-+  //msg_Dbg(obj, "Openal Close");
- 	audio_output_t *aout = (audio_output_t *)obj;
- 	struct aout_sys_t *sys = aout->sys;
- 
--- 
-2.23.0
-
diff --git a/vlc_patches/openal/0005-aout-Change-function-prototypes-and-add-volume_set-s.patch b/vlc_patches/openal/0005-aout-Change-function-prototypes-and-add-volume_set-s.patch
deleted file mode 100644
index a9c16270e2adbf273a1b52f5ab318d4f2f14572d..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0005-aout-Change-function-prototypes-and-add-volume_set-s.patch
+++ /dev/null
@@ -1,77 +0,0 @@
-From 5085d642685c1e47c78efeb9046373ca78f7b412 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Fri, 4 Oct 2019 19:37:18 +0200
-Subject: [PATCH 5/6] aout: Change function prototypes and add volume_set stub
-
-This commit enables a bug that is caused by an invalid pointer,
-that I couldn't find yet. This is not a good implementation, as
-audio will work, but will prevent frames from being displayed.
-Probably because of the Audio processing taking too much processing
-power out of the browser main thread.
----
- modules/audio_output/openal.c | 19 +++++++++++++------
- 1 file changed, 13 insertions(+), 6 deletions(-)
-
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-index 2a5f6f946d..374f2fd0a9 100644
---- a/modules/audio_output/openal.c
-+++ b/modules/audio_output/openal.c
-@@ -52,12 +52,13 @@
- static int  Open 		( vlc_object_t * );
- static void Close		( vlc_object_t * );
- static void Stop		( audio_output_t *);
--static void Play      	( audio_output_t *, block_t * );
-+static void Play      	        ( audio_output_t *, block_t * , vlc_tick_t);
- static void Flush		( audio_output_t *, bool );
--static void Pause		( audio_output_t *, bool , mtime_t );
-+static void Pause		( audio_output_t *, bool , vlc_tick_t);
- static int Time_Get		( audio_output_t *, mtime_t * );
- static int Start		( audio_output_t *, audio_sample_format_t *restrict );
- static int DeviceSelect	( audio_output_t *, const char *);
-+static int Volume_Set           ( audio_output_t *, float);
- //static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
- //                                vlc_value_t newval, vlc_value_t oldval, void *p_unused );
- //static void GetDevices( vlc_object_t *, module_config_t * );
-@@ -143,8 +144,7 @@ static int Open( vlc_object_t *obj )
- 	aout->pause = Pause;
- 	aout->flush = Flush;
- 	aout->time_get = Time_Get;
--	aout->pause = Pause;
--	aout->volume_set = NULL;
-+	aout->volume_set = Volume_Set;
- 	aout->mute_set = NULL;
- //	aout->device_select = DeviceSelect;
- 
-@@ -215,8 +215,9 @@ static void Stop (audio_output_t *aout)
- /*****************************************************************************
-  * Play: method description here
-  *****************************************************************************/
--static void Play( audio_output_t *aout, block_t *block )
-+static void Play( audio_output_t *aout, block_t *block , vlc_tick_t date)
- {
-+  VLC_UNUSED(date);
- 	aout_sys_t *sys = aout->sys;
- 	const void* data = block->p_buffer;
- 	size_t datalen = block->i_buffer;
-@@ -256,7 +257,7 @@ static void Play( audio_output_t *aout, block_t *block )
- 	block_Release(block);
- }
- 
--static void Pause( audio_output_t *aout, bool paused, mtime_t date )
-+static void Pause( audio_output_t *aout, bool paused, vlc_tick_t date )
- {
- 	aout_sys_t *sys = aout->sys;
- 	// TODO
-@@ -306,3 +307,9 @@ static void Close( vlc_object_t *obj )
- 	free(sys);
- }
- 
-+static int Volume_Set           ( audio_output_t *aout, float volume)
-+{
-+  VLC_UNUSED(aout);
-+  VLC_UNUSED(volume);
-+  return 0;
-+}
--- 
-2.23.0
-
diff --git a/vlc_patches/openal/0006-fixup-implement-timeget-stub-correctly.patch b/vlc_patches/openal/0006-fixup-implement-timeget-stub-correctly.patch
deleted file mode 100644
index 34de78ca34a61728ae52e853e266b28d48c32f53..0000000000000000000000000000000000000000
--- a/vlc_patches/openal/0006-fixup-implement-timeget-stub-correctly.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From b904cb2019ff494a49d249b52f50e71fd4d1e83b Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Fri, 11 Oct 2019 11:47:48 +0200
-Subject: [PATCH 6/6] fixup: implement timeget stub correctly
-
----
- modules/audio_output/openal.c | 8 +++-----
- 1 file changed, 3 insertions(+), 5 deletions(-)
-
-diff --git a/modules/audio_output/openal.c b/modules/audio_output/openal.c
-index 374f2fd0a9..48fdadb9dd 100644
---- a/modules/audio_output/openal.c
-+++ b/modules/audio_output/openal.c
-@@ -55,7 +55,7 @@ static void Stop		( audio_output_t *);
- static void Play      	        ( audio_output_t *, block_t * , vlc_tick_t);
- static void Flush		( audio_output_t *, bool );
- static void Pause		( audio_output_t *, bool , vlc_tick_t);
--static int Time_Get		( audio_output_t *, mtime_t * );
-+static int Time_Get		( audio_output_t *, vlc_tick_t * );
- static int Start		( audio_output_t *, audio_sample_format_t *restrict );
- static int DeviceSelect	( audio_output_t *, const char *);
- static int Volume_Set           ( audio_output_t *, float);
-@@ -288,11 +288,9 @@ static int DeviceSelect( audio_output_t *aout, const char *psz_device)
- 	return VLC_SUCCESS;
- }
- 
--static int Time_Get( audio_output_t *aout, mtime_t *delay)
-+static int Time_Get( audio_output_t *aout, vlc_tick_t *delay)
- {
--	aout_sys_t *sys = aout->sys;
--	*delay = aout_TimeGetDefault(aout, delay);
--	return 0;
-+	return aout_TimeGetDefault(aout, delay);
- }
- 
- /*****************************************************************************
--- 
-2.23.0
-
diff --git a/vlc_patches/video_output/0001-opengl-set-egl-display.patch b/vlc_patches/video_output/0001-opengl-set-egl-display.patch
new file mode 100644
index 0000000000000000000000000000000000000000..68c013c6ab9f64c5bf88493d5808d7043a1668f9
--- /dev/null
+++ b/vlc_patches/video_output/0001-opengl-set-egl-display.patch
@@ -0,0 +1,29 @@
+From 05d8b9c89724bb8cb1f268b8e9704d9673ec988c Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Tue, 27 Apr 2021 16:27:19 +0200
+Subject: [PATCH] opengl: set egl display
+
+Emscripten does not implement EGL extensions.
+https://emscripten.org/docs/porting/multimedia_and_graphics/EGL-Support-in-Emscripten.html#egl-extensions
+---
+ modules/video_output/opengl/egl_display_generic.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/modules/video_output/opengl/egl_display_generic.c b/modules/video_output/opengl/egl_display_generic.c
+index dfb841feef..730fb381d2 100644
+--- a/modules/video_output/opengl/egl_display_generic.c
++++ b/modules/video_output/opengl/egl_display_generic.c
+@@ -52,8 +52,8 @@ static vlc_egl_display_open_fn Open;
+ static int
+ Open(struct vlc_egl_display *display)
+ {
+-#ifdef __ANDROID__
+-    /* The default display is refcounted on Android */
++#if defined(__ANDROID__) || defined(__EMSCRIPTEN__)
++    /* The default display is refcounted on Android and Emscripten */
+     display->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ #elif defined(EGL_KHR_display_reference)
+     const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+-- 
+2.32.0
+
diff --git a/vlc_patches/video_output/0001-vout-add-emscripten-webgl-module.patch b/vlc_patches/video_output/0001-vout-add-emscripten-webgl-module.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0134518a56e3708d6f20734cf031b9c4bb424615
--- /dev/null
+++ b/vlc_patches/video_output/0001-vout-add-emscripten-webgl-module.patch
@@ -0,0 +1,223 @@
+From 9fbc02711de3798c7bd7beda0db515fae91088d5 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Tue, 27 Apr 2021 16:45:02 +0200
+Subject: [PATCH] vout: add emscripten webgl module
+
+this module uses the OFFSCREEN_FRAMEBUFFER option which
+adds a backbuffer to the webgl context, that will be used
+for rendering.
+
+it works as a polyfill for offscreen canvas.
+
+Co-Authored-By: Etienne Brateau <etienne.brateau@gmail.com>
+---
+ modules/video_output/Makefile.am  |   7 ++
+ modules/video_output/emscripten.c | 178 ++++++++++++++++++++++++++++++
+ 2 files changed, 185 insertions(+)
+ create mode 100644 modules/video_output/emscripten.c
+
+diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
+index 72f870b41c..7f629a8990 100644
+--- a/modules/video_output/Makefile.am
++++ b/modules/video_output/Makefile.am
+@@ -301,6 +301,13 @@ libcaca_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
+ EXTRA_LTLIBRARIES += libcaca_plugin.la
+ vout_LTLIBRARIES += $(LTLIBcaca)
+ 
++### Emscripten ###
++libemscripten_window_plugin_la_SOURCES = video_output/emscripten.c
++
++if HAVE_EMSCRIPTEN
++vout_LTLIBRARIES += libemscripten_window_plugin.la
++endif
++
+ ### Common ###
+ 
+ libflaschen_plugin_la_SOURCES = video_output/flaschen.c
+diff --git a/modules/video_output/emscripten.c b/modules/video_output/emscripten.c
+new file mode 100644
+index 0000000000..f940738a03
+--- /dev/null
++++ b/modules/video_output/emscripten.c
+@@ -0,0 +1,178 @@
++/**
++ * @file emscripten.c
++ * @brief Emscripten webgl video output for VLC media player
++ */
++/*****************************************************************************
++ * Copyright © 2020 VLC authors and VideoLAN
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as published by
++ * the Free Software Foundation; either version 2.1 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ *****************************************************************************/
++
++#ifdef HAVE_CONFIG_H
++# include <config.h>
++#endif
++
++#include <stdarg.h>
++
++#include <vlc_common.h>
++#include <vlc_plugin.h>
++#include <vlc_vout_window.h>
++#include <vlc_vout_display.h>
++#include <vlc_opengl.h>
++
++#include "./opengl/vout_helper.h"
++
++#include <emscripten.h>
++#include <emscripten/html5.h>
++#include <webgl/webgl2.h>
++// eglGetProcAddress
++#include <EGL/egl.h>
++
++extern emscripten_GetProcAddress(char *name);
++
++static const struct vout_window_operations ops = {
++	//TODO: Implement canvas operations
++	//vout_window_ReportSize() should be called from here
++};
++
++typedef struct gl_sys_t
++{
++	unsigned width;
++	unsigned height;
++
++	EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
++} gl_sys_t;
++
++static int OpenWindow(vout_window_t *wnd)
++{
++	wnd->type = VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL;
++	wnd->ops = &ops;
++
++	return VLC_SUCCESS;
++}
++
++static void *GetProcAddress(vlc_gl_t *gl, const char *name)
++{
++	VLC_UNUSED(gl);
++
++	return eglGetProcAddress(name);
++}
++static int MakeCurrent(vlc_gl_t *gl)
++{
++	gl_sys_t *sys = gl->sys;
++
++	if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS)
++        return VLC_EGENERIC;
++	return VLC_SUCCESS;
++}
++
++static void ReleaseCurrent(vlc_gl_t *gl)
++{
++	VLC_UNUSED(gl);
++	emscripten_webgl_make_context_current(0);
++}
++
++static void Swap(vlc_gl_t *gl)
++{
++	VLC_UNUSED(gl);
++	emscripten_webgl_commit_frame();
++}
++
++static void Resize(vlc_gl_t *gl, unsigned w, unsigned h)
++{
++	VLC_UNUSED(gl);
++	VLC_UNUSED(w);
++	VLC_UNUSED(h);
++}
++
++static void Close (vlc_gl_t *gl)
++{
++	free(gl->sys);
++}
++
++static int Open (vlc_gl_t *gl, unsigned width, unsigned height)
++{  
++	VLC_UNUSED(width), VLC_UNUSED(height);
++  
++	EmscriptenWebGLContextAttributes attr;
++
++	emscripten_webgl_init_context_attributes(&attr);
++	attr.majorVersion=2;
++	attr.minorVersion=0;
++	attr.explicitSwapControl = 1;
++	
++	vout_window_t *wnd = gl->surface;
++  
++	if (wnd->type != VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL)
++		goto error;
++
++	gl_sys_t *sys;
++	
++	gl->sys = sys = calloc(1, sizeof(*sys));
++	if (!sys)
++		return VLC_ENOMEM;
++  
++	sys->context = emscripten_webgl_create_context("#canvas", &attr);  
++	if (!sys->context) {
++		msg_Err(gl, "Failed to make context current");
++		goto error;
++	}
++
++	// Check that the WebGL context is valid
++	if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS) {
++		emscripten_log(EM_LOG_CONSOLE, "failed to make context current");
++		goto error;
++	}
++
++	// Release the context
++	emscripten_webgl_make_context_current(0);
++	wnd->handle.em_context = sys->context;
++
++	// Implement egl routines: 
++	gl->make_current = MakeCurrent;
++	gl->release_current = ReleaseCurrent;
++	gl->resize = Resize;
++	gl->swap = Swap;
++	gl->get_proc_address = GetProcAddress;
++	gl->destroy = Close;
++
++	return VLC_SUCCESS;
++error:
++	Close(gl);
++	return VLC_EGENERIC;
++}
++
++/*
++ * Module descriptor
++ */
++vlc_module_begin()
++	set_shortname(N_("Emscripten Window"))
++	set_description(N_("Emscripten drawing area"))
++	set_category(CAT_VIDEO)
++	set_subcategory(SUBCAT_VIDEO_VOUT)
++	set_capability("vout window", 10)
++	set_callbacks(OpenWindow, NULL)
++
++	add_submodule ()
++	set_shortname("Emscripten GL")
++	set_description(N_("Emscripten extension for OpenGL"))
++	set_category(CAT_VIDEO)
++	set_subcategory(SUBCAT_VIDEO_VOUT)
++	set_capability("opengl es2", 50)
++	set_callback(Open)
++	add_shortcut("em_webgl")
++vlc_module_end()
++
+-- 
+2.32.0
+
diff --git a/vlc_patches/0013-window-add-emscripten-type.patch b/vlc_patches/video_output/0001-vout-add-emscripten-window-and-webgl-context.patch
similarity index 58%
rename from vlc_patches/0013-window-add-emscripten-type.patch
rename to vlc_patches/video_output/0001-vout-add-emscripten-window-and-webgl-context.patch
index 623adfe5b0e2a16b779bb298f9b4a8bfc8c75d24..3fa87b2d749ebc97a84a628bf0e1b001366a73a4 100644
--- a/vlc_patches/0013-window-add-emscripten-type.patch
+++ b/vlc_patches/video_output/0001-vout-add-emscripten-window-and-webgl-context.patch
@@ -1,32 +1,32 @@
-From 09a5bb40c8e09967a07fdc9127337a9992e4afc9 Mon Sep 17 00:00:00 2001
-From: Mehdi Sabwat <mehdisabwat@gmail.com>
-Date: Thu, 12 Sep 2019 15:02:47 +0200
-Subject: [PATCH 13/15] window: add emscripten type
+From 463e0decd11348debe16ec70c46173231929f188 Mon Sep 17 00:00:00 2001
+From: Etienne Brateau <etienne.brateau@gmail.com>
+Date: Tue, 27 Apr 2021 16:19:40 +0200
+Subject: [PATCH] vout: add emscripten window and webgl context
 
 ---
  include/vlc_vout_window.h | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/include/vlc_vout_window.h b/include/vlc_vout_window.h
-index 9116fa6561..8533a963f3 100644
+index dd4e67a86e..769c3170d1 100644
 --- a/include/vlc_vout_window.h
 +++ b/include/vlc_vout_window.h
-@@ -63,6 +63,7 @@ enum vout_window_type {
-     VOUT_WINDOW_TYPE_NSOBJECT /**< MacOS X view */,
+@@ -64,6 +64,7 @@ enum vout_window_type {
      VOUT_WINDOW_TYPE_ANDROID_NATIVE /**< Android native window */,
      VOUT_WINDOW_TYPE_WAYLAND /**< Wayland surface */,
+     VOUT_WINDOW_TYPE_DCOMP /**< Win32 DirectComposition */,
 +    VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL /**< Emscripten surface */,
  };
  
  /**
-@@ -360,6 +361,7 @@ typedef struct vout_window_t {
-         void     *nsobject;      /**< Mac OSX view object */
+@@ -378,6 +379,7 @@ typedef struct vout_window_t {
          void     *anativewindow; /**< Android native window */
          struct wl_surface *wl;   /**< Wayland surface (client pointer) */
+         void     *dcomp_visual;  /**<  Win32 direct composition visual */
 +        uint32_t em_context;     /* Emscripten webgl context */
      } handle;
  
      /** Display server (mandatory)
 -- 
-2.23.0
+2.32.0
 
diff --git a/vlc_patches/video_output/offscreen-canvas.patch b/vlc_patches/video_output/offscreen-canvas.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4e9f4df6e959938443984aca2b7f9a914dba4549
--- /dev/null
+++ b/vlc_patches/video_output/offscreen-canvas.patch
@@ -0,0 +1,357 @@
+From f77f89feafc1c88aee29671964344a84ded89238 Mon Sep 17 00:00:00 2001
+From: Mehdi Sabwat <mehdi@videolabs.io>
+Date: Wed, 18 Nov 2020 20:34:30 +0100
+Subject: [PATCH 1/1] vout: add offscreen canvas initial support
+
+The Webgl context cannot be shared between workers, we need to be able to have :
+- shader and program creation and linking
+- gl operations
+in the same worker.
+---
+ modules/video_output/emscripten.c | 185 ++++++++++++++++--------------
+ src/video_output/video_output.c   |  20 ++++
+ src/video_output/vout_private.h   |   1 +
+ src/video_output/vout_wrapper.c   |   3 +
+ 4 files changed, 125 insertions(+), 84 deletions(-)
+
+diff --git a/modules/video_output/emscripten.c b/modules/video_output/emscripten.c
+index f940738a03..988c8a7818 100644
+--- a/modules/video_output/emscripten.c
++++ b/modules/video_output/emscripten.c
+@@ -44,135 +44,152 @@
+ extern emscripten_GetProcAddress(char *name);
+ 
+ static const struct vout_window_operations ops = {
+-	//TODO: Implement canvas operations
+-	//vout_window_ReportSize() should be called from here
++       //TODO: Implement canvas operations
++       //vout_window_ReportSize() should be called from here
+ };
+ 
+ typedef struct gl_sys_t
+ {
+-	unsigned width;
+-	unsigned height;
++  unsigned width;
++  unsigned height;
+ 
+-	EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
++  EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
+ } gl_sys_t;
+ 
+ static int OpenWindow(vout_window_t *wnd)
+ {
+-	wnd->type = VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL;
+-	wnd->ops = &ops;
++    wnd->type = VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL;
++    wnd->ops = &ops;
+ 
+-	return VLC_SUCCESS;
++    return VLC_SUCCESS;
+ }
+ 
+ static void *GetProcAddress(vlc_gl_t *gl, const char *name)
+ {
+-	VLC_UNUSED(gl);
++  VLC_UNUSED(gl);
+ 
+-	return eglGetProcAddress(name);
++  return (void *)emscripten_GetProcAddress(name);
+ }
+ static int MakeCurrent(vlc_gl_t *gl)
+ {
+-	gl_sys_t *sys = gl->sys;
+-
+-	if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS)
+-        return VLC_EGENERIC;
+-	return VLC_SUCCESS;
++    VLC_UNUSED(gl);
++  /*
++    We don't need to MakeCurrent or ReleaseCurrent in a single threaded setting
++    cf : https://chromium.googlesource.com/chromium/src/+/9478e129bf1ab74b7629d2837b88189d234587b7/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializer.cpp#416
++    https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenWebGLContextAttributes.proxyContextToMainThread
++    offscreen canvas means for now that all Webgl operations will happen in one thread
++  */ 
++
++  return VLC_SUCCESS;
+ }
+ 
+ static void ReleaseCurrent(vlc_gl_t *gl)
+ {
+-	VLC_UNUSED(gl);
+-	emscripten_webgl_make_context_current(0);
++  VLC_UNUSED(gl);
+ }
+ 
+ static void Swap(vlc_gl_t *gl)
+ {
+-	VLC_UNUSED(gl);
+-	emscripten_webgl_commit_frame();
++  VLC_UNUSED(gl);
++  EM_ASM({
++          // cf : vlc/extras/package/wasm-emscripten/assets/module-loader.js:postRun()
++          postMessage({ cmd: "objectTransfer", msg: { bitmap: self.Module.ctx.canvas.transferToImageBitmap()} });
++      }, 0);
+ }
+ 
+ static void Resize(vlc_gl_t *gl, unsigned w, unsigned h)
+ {
+-	VLC_UNUSED(gl);
+-	VLC_UNUSED(w);
+-	VLC_UNUSED(h);
++  // to implement
++  VLC_UNUSED(gl);
++  VLC_UNUSED(w);
++  VLC_UNUSED(h);
+ }
+ 
+ static void Close (vlc_gl_t *gl)
+ {
+-	free(gl->sys);
++  free(gl->sys);
+ }
+ 
+ static int Open (vlc_gl_t *gl, unsigned width, unsigned height)
+ {  
+-	VLC_UNUSED(width), VLC_UNUSED(height);
+-  
+-	EmscriptenWebGLContextAttributes attr;
+-
+-	emscripten_webgl_init_context_attributes(&attr);
+-	attr.majorVersion=2;
+-	attr.minorVersion=0;
+-	attr.explicitSwapControl = 1;
+-	
+-	vout_window_t *wnd = gl->surface;
++  VLC_UNUSED(width), VLC_UNUSED(height); // to implement
++    
++  vout_window_t *wnd = gl->surface;
+   
+-	if (wnd->type != VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL)
+-		goto error;
+-
+-	gl_sys_t *sys;
+-	
+-	gl->sys = sys = calloc(1, sizeof(*sys));
+-	if (!sys)
+-		return VLC_ENOMEM;
++  if (wnd->type != VOUT_WINDOW_TYPE_EMSCRIPTEN_WEBGL)
++    goto error;
++
++  gl_sys_t *sys;
+   
+-	sys->context = emscripten_webgl_create_context("#canvas", &attr);  
+-	if (!sys->context) {
+-		msg_Err(gl, "Failed to make context current");
+-		goto error;
+-	}
+-
+-	// Check that the WebGL context is valid
+-	if (emscripten_webgl_make_context_current(sys->context) != EMSCRIPTEN_RESULT_SUCCESS) {
+-		emscripten_log(EM_LOG_CONSOLE, "failed to make context current");
+-		goto error;
+-	}
+-
+-	// Release the context
+-	emscripten_webgl_make_context_current(0);
+-	wnd->handle.em_context = sys->context;
+-
+-	// Implement egl routines: 
+-	gl->make_current = MakeCurrent;
+-	gl->release_current = ReleaseCurrent;
+-	gl->resize = Resize;
+-	gl->swap = Swap;
+-	gl->get_proc_address = GetProcAddress;
+-	gl->destroy = Close;
+-
+-	return VLC_SUCCESS;
++  gl->sys = sys = (gl_sys_t *)(calloc(1, sizeof(*sys)));
++
++  if (!sys)
++    return VLC_EGENERIC;
++    
++  EM_ASM({
++          const wdt = $0;
++          const hgt = $1;
++          
++          var canvas = new OffscreenCanvas(wdt, hgt);
++          // EM_ASM are constants, we need this to reuse the variable in another scope
++          // as long as we stay in the worker 
++          Module.canvas = canvas;
++          
++          // set attributes with overloads for the emscripten GL object
++          var attributes = {};
++          attributes.alpha = true;
++          attributes.depth = true;
++          attributes.stencil = false;
++          attributes.antialias = true;
++          attributes.premultipliedAlpha = true;
++          attributes.preserveDrawingBuffer = true;
++          attributes.powerPreference = "default";// default
++          attributes.failIfMajorPerformanceCaveat=false;
++          attributes.majorVersion=2;
++          attributes.minorVersion=0;
++          attributes.enableExtenisonsByDefault=true;
++          attributes.explicitSwapControl=true;
++          attributes.renderViaOffscreenBackBuffer=false;
++          attributes.proxyContextToMainThread = 0;
++
++          // Create the context and set it as current
++          self.GLctx = GL.createContext(canvas, attributes);
++          GL.currentContext = self.GLctx;
++          GL.makeContextCurrent(self.GLctx);
++      }, width, height);
++  wnd->handle.em_context = sys->context;
++
++  // Implement egl routines: 
++  gl->make_current = MakeCurrent;
++  gl->release_current = ReleaseCurrent;
++  gl->resize = Resize;
++  gl->swap = Swap;
++  gl->get_proc_address = GetProcAddress;
++  gl->destroy = Close;
++
++  return VLC_SUCCESS;
+ error:
+-	Close(gl);
+-	return VLC_EGENERIC;
++  Close(gl);
++  return VLC_EGENERIC;
+ }
+ 
+ /*
+  * Module descriptor
+  */
+ vlc_module_begin()
+-	set_shortname(N_("Emscripten Window"))
+-	set_description(N_("Emscripten drawing area"))
+-	set_category(CAT_VIDEO)
+-	set_subcategory(SUBCAT_VIDEO_VOUT)
+-	set_capability("vout window", 10)
+-	set_callbacks(OpenWindow, NULL)
+-
+-	add_submodule ()
+-	set_shortname("Emscripten GL")
+-	set_description(N_("Emscripten extension for OpenGL"))
+-	set_category(CAT_VIDEO)
+-	set_subcategory(SUBCAT_VIDEO_VOUT)
+-	set_capability("opengl es2", 50)
+-	set_callback(Open)
+-	add_shortcut("em_webgl")
++    set_shortname(N_("Emscripten Window"))
++    set_description(N_("Emscripten drawing area"))
++    set_category(CAT_VIDEO)
++    set_subcategory(SUBCAT_VIDEO_VOUT)
++    set_capability("vout window", 10)
++    set_callbacks(OpenWindow, NULL)
++
++    add_submodule ()
++    set_shortname("Emscripten GL")
++    set_description(N_("Emscripten extension for OpenGL"))
++    set_category(CAT_VIDEO)
++    set_subcategory(SUBCAT_VIDEO_VOUT)
++    set_capability("opengl es2", 50)
++    set_callback(Open)
++    add_shortcut("em_webgl")
+ vlc_module_end()
+-
+diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c
+index 9a7f8f786d..28cbd30620 100644
+--- a/src/video_output/video_output.c
++++ b/src/video_output/video_output.c
+@@ -70,6 +70,9 @@ typedef struct vout_thread_sys_t
+ 
+     bool dummy;
+ 
++    vlc_video_context *my_ctx;
++    const vout_configuration_t *my_cfg;
++
+     /* Splitter module if used */
+     char            *splitter_name;
+ 
+@@ -166,6 +169,8 @@ typedef struct vout_thread_sys_t
+ 
+ } vout_thread_sys_t;
+ 
++static void vout_DisableWindow(vout_thread_sys_t *sys);
++
+ #define VOUT_THREAD_TO_SYS(vout) \
+     container_of(vout, vout_thread_sys_t, obj.obj)
+ 
+@@ -1777,6 +1782,14 @@ static void *Thread(void *object)
+     vout_thread_sys_t *vout = object;
+     vout_thread_sys_t *sys = vout;
+ 
++    if (vout_Start(vout, sys->my_ctx, sys->my_cfg))
++    {
++        msg_Err(sys->my_cfg->vout, "video output display creation failed");
++        video_format_Clean(&sys->original);
++        vout_DisableWindow(vout);
++        goto leave;
++    }
++
+     vlc_tick_t deadline = VLC_TICK_INVALID;
+     bool wait = false;
+ 
+@@ -1808,6 +1821,7 @@ static void *Thread(void *object)
+ 
+         vout_SetInterlacingState(&vout->obj, &sys->private, picture_interlaced);
+     }
++leave: (void *)0; // no-op
+     return NULL;
+ }
+ 
+@@ -1981,6 +1995,8 @@ vout_thread_t *vout_Create(vlc_object_t *object)
+     vout_thread_sys_t *sys = p_vout;
+     sys->dummy = false;
+ 
++    vlc_sem_init(&sys->private.my_sem, 0);
++
+     /* Register the VLC variable and callbacks. On the one hand, the variables
+      * must be ready early on because further initializations below depend on
+      * some of them. On the other hand, the callbacks depend on said
+@@ -2180,11 +2196,15 @@ int vout_Request(const vout_configuration_t *cfg, vlc_video_context *vctx, input
+         return -1;
+     }
+     atomic_store(&sys->control_is_terminated, false);
++    sys->my_ctx = vctx;
++    sys->my_cfg = cfg;
++    
+     if (vlc_clone(&sys->thread, Thread, vout, VLC_THREAD_PRIORITY_OUTPUT)) {
+         vout_ReleaseDisplay(vout);
+         vout_DisableWindow(vout);
+         return -1;
+     }
++    vlc_sem_wait(&sys->private.my_sem);
+ 
+     if (input != NULL && sys->spu)
+         spu_Attach(sys->spu, input);
+diff --git a/src/video_output/vout_private.h b/src/video_output/vout_private.h
+index 5e8c58dabe..24965748ed 100644
+--- a/src/video_output/vout_private.h
++++ b/src/video_output/vout_private.h
+@@ -41,6 +41,7 @@ struct vout_thread_private_t
+ 
+     picture_pool_t  *private_pool;
+     picture_pool_t  *display_pool;
++    vlc_sem_t my_sem;
+ };
+ 
+ /* */
+diff --git a/src/video_output/vout_wrapper.c b/src/video_output/vout_wrapper.c
+index 9d819f8a2d..9994aefc80 100644
+--- a/src/video_output/vout_wrapper.c
++++ b/src/video_output/vout_wrapper.c
+@@ -74,6 +74,9 @@ vout_display_t *vout_OpenWrapper(vout_thread_t *vout, vout_thread_private_t *sys
+         modlist = "splitter,none";
+ 
+     vd = vout_display_New(VLC_OBJECT(vout), fmt, vctx, cfg, modlist, &owner);
++
++    vlc_sem_post(&sys->my_sem);
++    
+     free(modlistbuf);
+ 
+     if (vd == NULL)
+-- 
+2.32.0
+