Recipes
Short procedures for the changes that come up most. Each assumes a checkout set up as in Set up a checkout.
Add or change a codec route
- Capability.
Add an exact-output probe to
src/capability/CustomDecodeCapabilities.ts. Give a video probe an identifier inCustomDecodeVideoProbeandCUSTOM_DECODE_VIDEO_PROBES, and select it inselectItemVideoProbesfor the streams that need it, or no item runs it. For an HEVC range extension:- add its definition to
src/capability/HEVCRangeExtensionCapabilities.ts; - add its vector to
scripts/codec_vector_assets/generate_HEVC_range_extension_vectors.pyand generate it intobin/codec_vector_assets/hevc-range-extension/; - add its served path to
src/EngineAssets.ts. The asset build serves every vector in that folder, and theEngineAssetstest checks that the two agree.
- add its definition to
- Presentation:
- add the route key to
src/validation/RawHDRPresentationAuthorization.ts, or to the external or Dolby Vision authorization beside it; - add the route’s built-in signal to
src/presentation/passes/builtin/BuiltinSignals.tsand its chain toBuiltinChains.ts; - add the plane uploads to
src/presentation/passes/PlaneTextureSource.ts, and the raw copy tosrc/decode/video/RawVideoFrameCopy.ts; - add any new raw format to
src/pipeline/DecodeWorkerProtocol.ts.
- add the route key to
- Runtime.
src/capability/CustomPlaybackEligibility.tsselects the route, andsrc/presentation/PresentationInput.tsparses the color and Dolby Vision descriptors. - Container pairing stays only in
src/capability/CustomContainerCodecSupport.ts. Add no resolution, level, frame rate, or bitrate gate, and no decoder pair blacklist. - Tests.
Cover the route’s selection and fallbacks in
test/capability/CustomPlaybackEligibility.test.ts. - Update HEVC and Dolby Vision support, and Eligibility and routes if the route catalog changed.
- Advertise the route in the host. The Jellyfin plugin’s book has the recipe for its device profile and its support matrix tests.
Check: the engine checks from Set up a checkout, and the host’s checks.
Add a codec that only a package decodes
- Name it as FFmpeg does, in
CUSTOM_VIDEO_CODECSandCUSTOM_PACKAGE_VIDEO_CODECSor in the audio codec lists, where its capability readsno-engine-decoder. Map the names a media server reports for it in both name tables, insrc/capability/CustomPlaybackEligibility.tsandCustomDecodeCapabilities.ts, and give it a case in every switch the type checker names. - Pair it with its containers in
src/capability/CustomContainerCodecSupport.ts. - For video, give the worker its frame-pool route in
src/pipeline/CustomDecode.worker.ts: the container codec IDs it reads, and the codec private data and packets a package receives, with any container reader undersrc/demux/. Describe that codec private data in Decoder packages, since every package of the codec depends on it. - Build a decoder project for it in
wgpud/<project>/, as the next recipe says, whose header accepts the codec’s name. - Cover the eligibility, the container reach, and the worker’s route in
test/, and the project in its owntest/.
Change or add a decoder project
- In the
wgpud/submodule, a checkout of the decoders repository, edit, or create,wgpud/<project>/: itsdecoder.json, its decoder source, whose header declares the project’s name as itsid:, and, for a module, aMakefilethat includes../rules.mkand buildsmodule. List its self-test vectors indecoder.json, one per output format at most, and runpython scripts/codec_vector_assets/generate_decoder_package_vectors.pyto write its vectors file. - Run
npm run decoders -- <project>; it needs the decoder toolchain (see WebAssembly build), and a C module needsEMSDKset or the engine’s emsdk frommake -C wasm emsdk. - Run its tests,
npx vitest --watch=false wgpud/<project>, which load the package frombin/wgpud/. - Run
npm run decoders -- --check, which must report every package identical. A change that is not reproducible shows up here. - Update the project table in WebAssembly build.
- Commit the change in the decoders repository and push it to
main, then release it with its Release workflow, as Release a repository says. - In the engine, run
node scripts/pinned-releases.mjs --pin decoders <tag>, move thewgpudsubmodule to the released commit, and commit the gitlink andtools/pinned-releases.jsonin one engine commit.
Change or add a pass project
- In the
wgpup/submodule, a checkout of the passes repository, edit, or create,wgpup/<project>/with its pass files: WGSL files at the project’s top level, each of one or more passes in the pass file format, under a file name no other project uses. A generated pass keeps its generator and inputs in the project and is committed, so a build needs no generator. An mpv user shader converts withtools/convert_mpv_shader.py(see Migrate an mpv shader to a wgpup). - For a new project, add it to
EXPECTED_PROJECTSintest/presentation/passes/PostprocessingPasses.test.ts. Runnpx vitest --watch=false test/presentation/passes, whose pass rules every pass file must keep. - Run
npm run passes, which copies the pass files intobin/wgpup/, and pick the pass in the sandbox’s shader panel to see it. - Update the project table in Bundled passes.
- Commit the change in the passes repository and push it to
main, then release it with its Release workflow, as Release a repository says. - In the engine, run
node scripts/pinned-releases.mjs --pin passes <tag>, move thewgpupsubmodule to the released commit, and commit the gitlink andtools/pinned-releases.jsonin one engine commit.
Rebuild one engine module
- Edit
wasm/<kit>/,libdovioraudio-output-stage. - Run
make -C wasm <kit>, withEMSDKset or the engine’s emsdk frommake -C wasm emsdk. - Run
npm test. - Run
make -C wasm check, which must report every output identical.
Change the Dolby Vision parser
- Edit
wasm/libdovi/src/lib.rs, or the vendored crate inwasm/libdovi/vendor/dolby_vision/. - For any change to the vendored crate, add an entry to its
PATCHES.mdnaming the file, the change, and the FFmpeg behavior it follows. - If the packed snapshot changes, update
src/demux/dolby-vision/DolbyVisionRPUDataLayout.ts, the decoder inDolbyVisionRPUParser.ts, the reshaping insrc/color/DolbyVisionColorTransform.ts, and the schema version inlib.rsandDolbyVisionRPUParser.tstogether. The revision prefix names the vendored dovi_tool commit and changes only when the copy moves to another upstream revision. - From
wasm/libdovi/, runcargo test --lockedandcargo clippy --locked --all-targets. - Run
make -C wasm libdovi, thenmake -C wasm check KITS=libdovi. - Run
npm test. The parser tests intest/demux/dolby-vision/run the rebuiltdovi-rpu-parser.wasm.
Revise a committed codec vector
- For an encoder-dependent vector (the TrueHD sources, the JPEG 2000 decoder vectors), delete the committed file. A differing encode never replaces one.
- Run its generator with the pinned FFmpeg on PATH, for example
python scripts/codec_vector_assets/generate_truehd_capability_vectors.py --regenerate-sources. A stream a decoder project’sdecoder.jsonlists also needspython scripts/codec_vector_assets/generate_decoder_package_vectors.py <project>, which rewrites that project’s self-test vectors. - Run
python scripts/generate_all_codec_vector_assets.py --checkand the generator tests. - Review the diff of
bin/codec_vector_assets/and of thewgpud/submodule, and update any pinned fingerprint or length only after inspecting the decoded output. A changed decoder project’s vectors are committed in the decoders repository and reach the engine as the recipe Change or add a decoder project says.
Move an engine folder
- Change the folder in
tools/constants.json. Every reader in Repository layout follows. - Edit by hand the files that cannot read it:
.gitignore,.gitattributes,.gitmodules, andtsconfig.json, whosepathsentry names the Mediabunny fork’s folder;sandbox/vite.config.ts, whose alias names that folder too, and the host’s webpack, Vitest, and tsconfig aliases of it;package.json: its scripts, and itsimportsmap, which sends#codec_vector_assets/*to the generated modules;- each decoder project’s
decoder.json, whose vectors name a layout key and a path under it; - this book.
- Rerun the DTS and TrueHD generators, which embed import paths computed from the constants, then run every
--checkand the full engine checks.
Capture a timing trace
- Call
startTimingTrace()fromwebgpu-player/TimingTracebefore the play request, so the decode run the play starts is asked for its events. A run started before the trace sends none; the next seek’s run does. - Play the passage under investigation.
- Call
exportTimingTrace(metadata)and save the result as JSON.stopTimingTrace()stops recording but keeps the events exportable until the next start orclearTimingTrace(). - Read the events in time order.
timeMillisecondscounts from the trace start,realmsays whether the page or the decode worker recorded it, anddroppedEventCountis nonzero when the ring buffer overwrote the oldest events.
Ship an engine change to the host
Commit the change in the engine repository and push it before the host commits the new submodule pointer, so the host never pins a commit nobody can fetch.