#encoder.js
Encoder.js uses the emscripten crosscompiler to compile encoder.c, underlying FFMPEG libraries, and the pre.js and post.js javascript wrappers into asm.js code which can be run in a browser. It works great in Chrome, Firefox, Edge, and Safari and encodes audio at 3x real time. It runs sloooowly in Internet Explorer (1/6 real time) because that browser does not have the asm.js optimizations. For Internet Explorer, see the ActionScript version Encoder.swc instead.
The JavaScript cross compiled version of the encoder is bundled as an npm package and is intended to run in a
web worker thread.
The build.sh script only enables the pcm_f32le
decoder for raw microphone input, the aac
codec, and the mp4
format muxer.
Change the ./configure
options in the build.sh script to support output codecs and file formats.
The encoder has a custom C wrapper around the libav libraries from FFMPEG and does not use the actual ffmpeg
program.
In assets/draft/jsencoder/ffmpegwrapper.js
you can see an earlier implementation which used the ffmpeg
program, Module['arguments']
,
and the emscripten filesystem
to pass audio in and out of the encoder.
Note that using this approach also requires changing the ./configure
options in build.sh
to build the ffmpeg
program and use the .bc
suffix.
See the ffmpeg.js project for a great example of this alternative approach.
I went with a custom C wrapper because it was an easier way to stream the PCM data into the encoder that way instead of using stdin
or the FFMPEG pipe
protocol.
Raw PCM data from the getUserMedia() microphone is passed to the JavaScript worker as a Float32Array.buffer
.
Output from the worker is a Uint8Array.buffer
which can be loaded into a Blob
and played in the browser using the Audio
element.
See frontend/scripts/factories/EncoderFactory.js
for usage and examples of the JSON objects used to transfer messages and data back and forth from the web worker.
You can follow instructions on the emscripten website or use homebrew to install emscripten.
Emscripten does not play well with cygwin as it gets mixed up with windows vs. cygwin paths internally. However, running it from MinGW works just fine.
Install MinGW. A recommended way to proceed is via installing Git Bash which installs MinGW along with it.
As a temporary patch until this issue is addressed by emscripten, the shared.py
file of the project has to be edited. Navigate to the base dir of the emscripten installation (the directory named Emscripten
). Then navigate to the shared.py
file (.../Emscripten/emscripten/1.35.0/tools/shared.py
). In that file, go inside the make
function to this if statement found at around line 1189:
if 'mingw32-make' in args[0]:
env = Building.remove_sh_exe_from_path(env)
Then, disable that if statement by adding the condition and False
to the end of it.
Now, to test the emcc installation, run emcc -v
.
Emcc may complain that it is unable find python2. So make sure that python is installed, cd to the installation directory, and make a soft link from python.exe
to python2.exe
.
In order to build this library, make and related programs are required. Download and run the mingw-get setup executable that will install mingw-get
, a MinGW library installation tool. Run the tool and select mingw-base
for installation.
Add the directory containing the mingw binaries (usually '/c/MinGW/bin') to PATH.
Then see if mingw32-make
exists as an executable runnable directly from MinGW.
If using homebrew on OS X, do the following to install emscripten and generate the ~/.emscripten config file:
brew install emscripten binaryen node yuicompressor
emcc -v
If you get errors which say python2 not found
, try setting up a link to python2.
Skip this step if you have no error.
ln -sf /usr/bin/python2.7 /usr/local/bin/python2
If you get errors which say emcc.py not found
, then you may need to manually create symlinks for the emcc executables.
Skip this step if you have no error.
ln -s /usr/local/opt/emscripten/libexec/em++ /usr/local/opt/emscripten/libexec/em++.py
ln -s /usr/local/opt/emscripten/libexec/emar /usr/local/opt/emscripten/libexec/emar.py
ln -s /usr/local/opt/emscripten/libexec/emcc /usr/local/opt/emscripten/libexec/emcc.py
ln -s /usr/local/opt/emscripten/libexec/emcmake /usr/local/opt/emscripten/libexec/emcmake.py
ln -s /usr/local/opt/emscripten/libexec/emconfigure /usr/local/opt/emscripten/libexec/emconfigure.py
ln -s /usr/local/opt/emscripten/libexec/emmake /usr/local/opt/emscripten/libexec/emmake.py
Now follow instructions which appeared in the homebrew installation message about editing the path to LLVM in the ~/.emscripten
config file.
Change the LLVM_ROOT property in ~/.emscripten
from /usr/bin
to be /usr/local/opt/emscripten/libexec/llvm/bin
. Also set the BINARYEN path to be /usr/local/opt/binaryen
.
Confirm emscripten properly installed by running the sanity check.
emcc -v
You should see no errors in the emscripten sanity check output.
Now execute the full build which compiles the base FFMPEG libraries, compiles the encoder.js wrapper,
and installs the output into the node_modules/pcmencoder folder in the frontend project.
Run the build.sh script in the encoder/js/
folder.
./build.sh
This will take a long time. The full build only needs to be done once to install the base FFMPEG libraries into the dist folder. When doing development on the encoder, you can do faster compilation of just the custom encoder.c, pre.js, and post.js code by running:
make clean
make
make install
- Get Jasmine test functioning with Web Worker implementation, blocked by node-webworker ArrayBuffer bug