# Reproducing this build

No prebuilt WebAssembly Theora *encoder* exists anywhere public — ogv.js
(`bvibber/ogv.js`) compiles libtheora to wasm too, but only ever wires up the
**decoder** half (`th_decode_*`); its own `Makefile` has no encode target at
all. This build exists because nothing else does.

Everything below used [emsdk](https://github.com/emscripten-core/emsdk)
(tested against emcc 6.0.7) and needed no other tools — deliberately: the
machine this was built on had no `make`, `autoconf`, `automake`, `libtool` or
`pkg-config` on it, so this drives `emcc` directly over an explicit file list
rather than through libtheora's own `./configure && make`.

## 1. Sources

Release tarballs, not git checkouts — a tarball ships a pre-generated
`./configure`, which sidesteps needing autoconf/automake at all:

- `https://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.gz`
- `https://downloads.xiph.org/releases/theora/libtheora-1.1.1.tar.bz2`

## 2. Generate libogg's config header

The only thing actually needed from libogg's own build system — Theora's
public headers pull in `ogg/ogg.h`, which needs `ogg/config_types.h` to exist.
`emmake make` was never reached (no `make`), but `emconfigure ./configure`
alone already writes this file:

```bash
mkdir build-ogg && cd build-ogg
emconfigure "C:/Program Files/Git/usr/bin/bash.exe" ../libogg/configure \
    --prefix="$PWD/../out/root" --disable-shared --enable-static \
    --disable-dependency-tracking CFLAGS="-O3"
```

(The explicit bash.exe path works around emscripten's Python launching
`configure` — a `#!/bin/sh` script — directly via `CreateProcess`, which
fails on native Windows with "not a valid Win32 application"; and even once
routed through `bash`, plain `bash` on `PATH` can resolve to the WSL launcher
stub instead of Git Bash's own, hence the full path.)

## 3. Compile straight to wasm, no intermediate `.a`

The full file lists below are exactly what
`libogg/src/Makefile.am` (`libogg_la_SOURCES`) and
`libtheora/lib/Makefile.am` (`libtheoraenc_la_SOURCES`, expanded for a
non-x86 target — `encoder_uniq_arch_sources`/`encoder_shared_arch_sources`
are both empty when `CPU_x86_64`/`CPU_x86_32` are false) say belong in
libogg and libtheoraenc respectively, plus this directory's own
`theora_enc_wrapper.c`. `info.c` matters and is easy to miss — the header
docs call `th_info_init()` etc. "functions shared by encode and decode", but
the file that defines them is only listed under `decoder_sources` in the
Makefile, despite `libtheoraenc_la_SOURCES` needing it too (the codec.h
comment "you must also link to libtheoradec" is the tell).

```bash
emcc -O3 \
  -I build-ogg/include -I libogg/include \
  -I libtheora/include -I libtheora/lib \
  -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s NO_EXIT_RUNTIME=1 \
  -s MODULARIZE=1 -s EXPORT_NAME="'TheoraEncoderModule'" \
  -s ENVIRONMENT=web,worker -s ALLOW_MEMORY_GROWTH=1 -s SINGLE_FILE=0 \
  -s EXPORTED_FUNCTIONS=_malloc,_free \
  -s "EXPORTED_RUNTIME_METHODS=['ccall','cwrap','HEAPU8','HEAP32']" \
  libogg/src/framing.c libogg/src/bitwise.c \
  libtheora/lib/apiwrapper.c libtheora/lib/fragment.c libtheora/lib/idct.c \
  libtheora/lib/internal.c libtheora/lib/state.c libtheora/lib/quant.c \
  libtheora/lib/info.c \
  libtheora/lib/analyze.c libtheora/lib/fdct.c libtheora/lib/encfrag.c \
  libtheora/lib/encapiwrapper.c libtheora/lib/encinfo.c libtheora/lib/encode.c \
  libtheora/lib/enquant.c libtheora/lib/huffenc.c libtheora/lib/mathops.c \
  libtheora/lib/mcenc.c libtheora/lib/rate.c libtheora/lib/tokenize.c \
  theora_enc_wrapper.c \
  -o theora-enc.js
```

## 4. Verify before trusting it

Node can load the `web,worker` build directly by passing `wasmBinary`
explicitly (it has no `fetch`-with-a-base-URL to resolve the wasm's relative
path against, and no `fs` fallback since `node` isn't in `ENVIRONMENT`) — or
rebuild once with `node` added to `ENVIRONMENT` for a throwaway test copy.
Either way: encode a handful of synthetic YUV420 frames, hand-mux the
resulting packets into an Ogg stream (page framing, CRC-32 — see the
`OggMux`/`makeOggPage` code in `media-converter.js`, this build's only
consumer), write the file, and let a completely independent decoder be the
judge:

```bash
ffprobe -show_streams out.ogv       # codec_name=theora, probe_score=100
ffmpeg -xerror -i out.ogv -map 0 -f null -   # exit 0 = every frame decoded clean
```

This is exactly how this build was checked — `ffmpeg`/`ffprobe` happened to
already be on the build machine's `PATH`, and turned out to be exactly what
was needed to prove a hand-rolled encoder wrapper and a hand-rolled muxer both
work without ever opening a video player.
