### Install Dependencies
Source: https://github.com/privaloops/hevc.js/blob/main/site/docs/dashjs-plugin.html
Install the necessary packages for the HEVC plugin and dash.js.
```bash
$ npm install @hevcjs/dashjs-plugin dashjs
```
--------------------------------
### Install and Build Demo
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Installs project dependencies, builds WASM and JS bundles for the demo, and serves the demo locally.
```bash
pnpm install
pnpm build:demo
npx serve demo
```
--------------------------------
### Install @hevcjs/dashjs-plugin and dashjs
Source: https://github.com/privaloops/hevc.js/blob/main/packages/dashjs-plugin/README.md
Install the necessary packages using npm. This command installs both the HEVC plugin and the dash.js library.
```bash
npm install @hevcjs/dashjs-plugin dashjs
```
--------------------------------
### Clone and Install Project Dependencies
Source: https://github.com/privaloops/hevc.js/blob/main/CONTRIBUTING.md
Clone the hevc.js repository and install its dependencies using pnpm. This is the initial step for setting up the development environment.
```bash
git clone https://github.com/privaloops/hevc.js.git
cd hevc.js
pnpm install
```
--------------------------------
### Install HEVC.js Plugins
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Install the appropriate HEVC.js plugin for your player using npm.
```bash
npm install @hevcjs/dashjs-plugin # dash.js
npm install @hevcjs/shaka-plugin # Shaka Player
```
--------------------------------
### Initial Page Load Setup
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Initializes WebGL and the Web Worker when the page loads. Displays an error if WebGL is not available.
```javascript
if (initGL()) {
fetch('hevc-decode.wasm').then(r => {
window._wasmSize = +r.headers.get('content-length') || 0;
});
initWorker();
} else {
setStatus('WebGL not available');
}
```
--------------------------------
### Install Shaka Player and HEVC Plugin
Source: https://github.com/privaloops/hevc.js/blob/main/packages/shaka-plugin/README.md
Install the necessary packages for Shaka Player and the HEVC plugin using npm.
```bash
npm install @hevcjs/shaka-plugin shaka-player
```
--------------------------------
### Install @hevcjs/core via npm
Source: https://github.com/privaloops/hevc.js/blob/main/packages/core/README.md
Install the core package using npm. This is the standard way to include the library in your project.
```bash
npm install @hevcjs/core
```
--------------------------------
### Setup dash.js with HEVC.js Plugin
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Integrate the HEVC.js plugin with dash.js by attaching the support and initializing the player with the worker URL.
```javascript
import dashjs from 'dashjs';
import { attachHevcSupport } from '@hevcjs/dashjs-plugin';
const player = dashjs.MediaPlayer().create();
attachHevcSupport(player, { workerUrl: './transcode-worker.js' });
player.initialize(videoElement, 'https://example.com/manifest.mpd', true);
```
--------------------------------
### Initialize Shaka Player and Load Stream
Source: https://github.com/privaloops/hevc.js/blob/main/demo/shaka.html
Initialize a Shaka Player instance and attach it to a video element. Then, load a Media Presentation Description (MPD) stream. This is a basic setup after registering the HEVC transmuxer.
```javascript
const player = new shaka.Player();
await player.attach(videoElement);
await player.load(mpdUrl);
```
--------------------------------
### Setup Shaka Player with HEVC.js Plugin
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Register the HEVC.js transmuxer with Shaka Player and attach compute-aware ABR. Ensure the worker URL is correctly provided.
```javascript
import shaka from 'shaka-player';
import { registerHevcTransmuxer } from '@hevcjs/shaka-plugin';
const handle = registerHevcTransmuxer(shaka, { workerUrl: './transcode-worker.js' });
const player = new shaka.Player();
await player.attach(videoElement);
handle.attachComputeAware(player); // wire compute-aware ABR (on by default)
await player.load('https://example.com/manifest.mpd');
```
--------------------------------
### Console Logging Setup
Source: https://github.com/privaloops/hevc.js/blob/main/demo/dash.html
Redirects console.log, console.warn, and console.error to a dedicated log element and prefixes messages. This is useful for debugging and monitoring player behavior.
```javascript
const logEl = document.getElementById('log'); function log(...args) { const line = args.map(a => { if (a instanceof Error) return `${a.name}: ${a.message}`; if (typeof a === 'object') return JSON.stringify(a); return String(a); }).join(' '); logEl.value += line + '\n'; logEl.scrollTop = logEl.scrollHeight; } const _log = console.log, _warn = console.warn, _err = console.error; console.log = (...a) => { _log(...a); log('[log]', ...a); }; console.warn = (...a) => { _warn(...a); log('[warn]', ...a); }; console.error = (...a) => { _err(...a); log('[ERROR]', ...a); };
```
--------------------------------
### Load @hevcjs/core from CDN
Source: https://github.com/privaloops/hevc.js/blob/main/packages/core/README.md
Load the library directly from a CDN for a zero-build setup. This method is useful for quick integration or when a build step is not desired. Ensure the `wasmBinaryUrl` is specified if assets are on a different origin.
```html
```
--------------------------------
### Dash.js Player Event Handlers
Source: https://github.com/privaloops/hevc.js/blob/main/demo/dash.html
Sets up event listeners for dash.js player events like quality change, stream initialization, and playback start. It also includes error handling and checks for WebCodecs support.
```javascript
// Active variant (refreshed when dash.js switches quality). function refreshActive() { const idx = player.getQualityFor?.('video'); const list = player.getBitrateInfoListFor?.('video') || []; const active = list[idx]; if (active) { document.getElementById('cmp-quality').textContent = 'quality: ' + (active.height ? active.height + 'p' : '?') + ' · ' + Math.round((active.bitrate || 0) / 1000) + ' kbps'; } } player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, refreshActive); player.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, refreshActive); player.on(dashjs.MediaPlayer.events.PLAYBACK_STARTED, () => { document.getElementById('status').textContent = 'Playing via dash.js + hevc.js'; }); // Warn if HEVC transcoding is unavailable (Firefox — VideoEncoder H.264 broken) player.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, () => { if (typeof VideoEncoder === 'undefined') { document.getElementById('status').textContent = 'This browser does not support WebCodecs. HEVC transcoding is not available. Try Chrome or Edge 94+.'; return; } VideoEncoder.isConfigSupported({ codec: 'avc1.42001f', width: 320, height: 240 }).then(r => { if (!r.supported) { document.getElementById('status').textContent = 'This browser cannot encode H.264 via WebCodecs. HEVC transcoding is not available. Try Chrome or Edge 94+.'; } }).catch(() => {}); }); player.on(dashjs.MediaPlayer.events.ERROR, (e) => { document.getElementById('status').textContent = 'Error: ' + (e.error?.message || JSON.stringify(e.error)); }); player.initialize(video, url, true); document.getElementById('status').textContent = 'Loading ' + url + '...'; }
```
--------------------------------
### Logging Setup for Diagnostics
Source: https://github.com/privaloops/hevc.js/blob/main/demo/shaka.html
Redirects console logs (log, warn, error) to a textarea element for debugging and diagnostics. This helps in monitoring Shaka Player's behavior and the HEVC transmuxer's activity.
```javascript
const logEl = document.getElementById('log');
function log(...args) {
const line = args.map(a => {
if (a instanceof Error) return `${a.name}: ${a.message}`;
if (typeof a === 'object') return JSON.stringify(a);
return String(a);
}).join(' ');
logEl.value += line + '\n';
logEl.scrollTop = logEl.scrollHeight;
}
const _log = console.log, _warn = console.warn, _err = console.error;
console.log = (...a) => {
_log(...a);
log('[log]', ...a);
};
console.warn = (...a) => {
_warn(...a);
log('[warn]', ...a);
};
console.error = (...a) => {
_err(...a);
log('[ERROR]', ...a);
};
```
--------------------------------
### Attach HEVC Support to dash.js Player
Source: https://github.com/privaloops/hevc.js/blob/main/site/index.html
Integrates HEVC playback capabilities into a dash.js player instance. Ensure the plugin is installed via npm.
```javascript
import {
attachHevcSupport
} from '@hevcjs/dashjs-plugin';
await attachHevcSupport(player);
```
--------------------------------
### Initialize dash.js Player with HEVC Support (Bundled)
Source: https://github.com/privaloops/hevc.js/blob/main/packages/dashjs-plugin/README.md
Import and attach the HEVC support to the dash.js player. Ensure the worker and WASM URLs are correctly set to the copied static assets. This code initializes the player with a given video element and manifest URL.
```javascript
import dashjs from 'dashjs';
import { attachHevcSupport } from '@hevcjs/dashjs-plugin';
const video = document.querySelector('video');
const player = dashjs.MediaPlayer().create();
await attachHevcSupport(player, {
workerUrl: '/transcode-worker.js',
wasmUrl: '/hevc-decode.js',
});
player.initialize(video, 'https://example.com/stream/manifest.mpd', true);
```
--------------------------------
### Run End-to-End Browser Tests
Source: https://github.com/privaloops/hevc.js/blob/main/CONTRIBUTING.md
Builds the demo bundles and then runs the end-to-end browser tests. This requires both the WASM build and demo assets to be ready.
```bash
# E2E browser tests (requires built WASM + demo bundles)
pnpm build:demo
pnpm test:e2e
```
--------------------------------
### Play/Pause Video Playback
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Toggles video playback. Starts or stops an interval timer to cycle through frames.
```javascript
document.getElementById('btn-play').addEventListener('click', () => {
if (playing) {
playing = false;
clearInterval(playTimer);
document.getElementById('btn-play').textContent = 'Play';
} else {
playing = true;
document.getElementById('btn-play').textContent = 'Pause';
playTimer = setInterval(() => {
if (currentFrame >= frames.length - 1) currentFrame = -1;
showFrame(currentFrame + 1);
}, 40);
}
});
```
--------------------------------
### Load and Decode Sample File
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Fetches a sample HEVC file ('bbb1080_singleslice.265') and sends it to the worker for decoding.
```javascript
document.getElementById('use-sample').addEventListener('click', async (e) => {
e.stopPropagation();
setStatus('Downloading sample...');
const resp = await fetch('bbb1080_singleslice.265');
const data = await resp.arrayBuffer();
decodeStartTime = performance.now();
worker.postMessage({ type: 'decode', data }, [data]);
setStatus('Decoding sample...');
});
```
--------------------------------
### Configure Unit Tests with Google Test
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
This snippet sets up Google Test for unit testing. It declares, fetches, and makes Google Test available, then adds an executable for the tests and links it against the main library and the GTest main library.
```cmake
if(BUILD_TESTS AND NOT BUILD_WASM)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(hevc_tests
tests/unit/test_bitstream_reader.cpp
tests/unit/test_nal_parser.cpp
tests/unit/test_picture.cpp
tests/unit/test_parameter_sets.cpp
tests/unit/test_cabac.cpp
tests/unit/test_incremental.cpp
)
target_link_libraries(hevc_tests PRIVATE
hevc-decode-lib
GTest::gtest_main
)
target_compile_definitions(hevc_tests PRIVATE
FIXTURES_DIR="${CMAKE_SOURCE_DIR}/tests/conformance/fixtures"
)
include(GoogleTest)
gtest_discover_tests(hevc_tests)
endif()
```
--------------------------------
### Initialize dash.js Player with HEVC Support (CDN)
Source: https://github.com/privaloops/hevc.js/blob/main/packages/dashjs-plugin/README.md
Load the HEVC plugin from a CDN and attach it to the dash.js player. This method is suitable for environments without a build pipeline. Specify URLs for worker, WASM loader, and WASM binary, especially for cross-origin loading.
```html
```
--------------------------------
### WebGL Initialization and Texture Upload
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Initializes the WebGL context, compiles shaders, sets up buffers, and creates textures for Y, Cb, and Cr color planes. Includes functions to upload texture data.
```javascript
let gl, program, texY, texCb, texCr, uScale;
let worker;
let frames = [];
let streamInfo = null;
let currentFrame = 0;
let playing = false;
let playTimer = null;
let decodeStartTime = 0;
let decodeTimeMs = 0;
function initGL() {
const canvas = document.getElementById('display');
gl = canvas.getContext('webgl');
if (!gl) {
setStatus('WebGL not supported');
return false;
}
const vs = compileShader(gl.VERTEX_SHADER, VERTEX_SRC);
const fs = compileShader(gl.FRAGMENT_SHADER, FRAGMENT_SRC);
program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram(program);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1,-1, 0,1, 1,-1, 1,1, -1,1, 0,0, 1,1, 1,0 ]), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(program, 'a_pos');
const aTex = gl.getAttribLocation(program, 'a_tex');
gl.enableVertexAttribArray(aPos);
gl.enableVertexAttribArray(aTex);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
gl.vertexAttribPointer(aTex, 2, gl.FLOAT, false, 16, 8);
texY = createTexture(0);
texCb = createTexture(1);
texCr = createTexture(2);
gl.uniform1i(gl.getUniformLocation(program, 'u_texY'), 0);
gl.uniform1i(gl.getUniformLocation(program, 'u_texCb'), 1);
gl.uniform1i(gl.getUniformLocation(program, 'u_texCr'), 2);
uScale = gl.getUniformLocation(program, 'u_scale');
return true;
}
function compileShader(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
return s;
}
function createTexture(unit) {
gl.activeTexture(gl.TEXTURE0 + unit);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return tex;
}
function uploadPlane(tex, unit, data, width, height, bitDepth) {
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, tex);
const scale = bitDepth <= 8 ? 1 : (255 / ((1 << bitDepth) - 1));
const u8 = new Uint8Array(width * height);
for (let i = 0; i < width * height; i++) {
u8[i] = Math.min(255, Math.round(data[i] * scale));
}
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, u8);
}
function renderFrame(frame) {
const canvas = document.getElementById('display');
canvas.width = frame.width;
canvas.height = frame.height;
gl.viewport(0, 0, frame.width, frame.height);
gl.uniform1f(uScale, 1.0);
uploadPlane(texY, 0, frame.dataY, frame.width, frame.height, frame.bitDepth);
uploadPlane(texCb, 1, frame.dataCb, frame.width >> 1, frame.height >> 1, frame.bitDepth);
uploadPlane(texCr, 2, frame.dataCr, frame.width >> 1, frame.height >> 1, frame.bitDepth);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
```
--------------------------------
### Add Oracle Test for Toy Bitstream (QP45)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds a specific oracle test for a toy bitstream with QP45. This test is labeled 'oracle', 'phase4', and 'toy'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_toy_qp45
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/toy_qp45.265 64 64
52ca5108bd4354383a9d29d96cf64b47 $)
set_tests_properties(oracle_toy_qp45 PROPERTIES LABELS "oracle;phase4;toy"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Enable MSE Intercept for HEVC Playback
Source: https://github.com/privaloops/hevc.js/blob/main/packages/core/README.md
Use the `installMSEIntercept` function to enable transparent HEVC playback using Media Source Extensions. Ensure the worker and WASM URLs are correctly configured for your server setup.
```javascript
import { installMSEIntercept } from '@hevcjs/core';
installMSEIntercept({
workerUrl: '/transcode-worker.js',
wasmUrl: '/hevc-decode.js',
});
```
--------------------------------
### Register HEVC Transmuxer with Shaka Player
Source: https://github.com/privaloops/hevc.js/blob/main/demo/shaka.html
Import and register the HEVC transmuxer plugin for Shaka Player. This allows Shaka Player to handle HEVC streams by transcoding them to H.264 using WASM. This snippet assumes a zero-build setup loading from a CDN.
```javascript
import { registerHevcTransmuxer } from 'https://esm.sh/@hevcjs/shaka-plugin@0';
registerHevcTransmuxer(shaka);
```
--------------------------------
### HEVC Plugin API and Configuration Options
Source: https://github.com/privaloops/hevc.js/blob/main/packages/dashjs-plugin/README.md
Demonstrates the usage of the `attachHevcSupport` function with various configuration options. The function returns a cleanup callback to remove patches when no longer needed.
```typescript
const cleanup = await attachHevcSupport(player, {
workerUrl: '/transcode-worker.js', // URL to the Web Worker script
wasmUrl: '/hevc-decode.js', // URL to the Emscripten loader
wasmBinaryUrl: '/hevc-decode.wasm', // URL to the .wasm binary — required for cross-origin loading
fps: 25, // Target framerate (optional, default: 25)
bitrate: 4_000_000, // H.264 encode bitrate (optional)
forceTranscode: false, // Bypass native HEVC detection (optional)
adaptiveCompute: true, // Compute-aware ABR (default true; pass false to opt out)
});
// Remove patches when done (also detaches the compute-aware listener)
cleanup();
```
--------------------------------
### Initialize dash.js with hevc.js Support
Source: https://github.com/privaloops/hevc.js/blob/main/demo/dash.html
This snippet shows how to import and attach hevc.js support to a dash.js MediaPlayer instance. It requires specifying URLs for the WASM decoder, WASM binary, and the transcode worker. Use this when you need to play HEVC content via dash.js and want to leverage WASM for transcoding.
```javascript
import { attachHevcSupport } from 'https://esm.sh/@hevcjs/dashjs-plugin@1';
const player = dashjs.MediaPlayer().create();
await attachHevcSupport(player, {
wasmUrl: 'https://unpkg.com/@hevcjs/core@1/dist/wasm/hevc-decode.js',
wasmBinaryUrl: 'https://unpkg.com/@hevcjs/core@1/dist/wasm/hevc-decode.wasm',
workerUrl: 'https://unpkg.com/@hevcjs/core@1/dist/transcode-worker.js',
});
player.initialize(videoElement, mpdUrl, true);
```
--------------------------------
### Native Build and Test
Source: https://github.com/privaloops/hevc.js/blob/main/CONTRIBUTING.md
Builds the project for native execution with debug symbols and runs C++ unit and oracle tests. This is useful for local development and debugging.
```bash
# Native build (debug + tests)
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
cd build && ctest --output-on-failure
```
--------------------------------
### Run Native Tests
Source: https://github.com/privaloops/hevc.js/blob/main/CONTRIBUTING.md
Executes the C++ unit and oracle tests for the project. This command should be run after a successful native build.
```bash
# C++ unit + oracle tests (128 tests)
pnpm test:native
```
--------------------------------
### Add Oracle Test for Phase 5 (P-frames, QCIF, 10 frames)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds an oracle test for Phase 5, focusing on P-frames for a QCIF resolution over 10 frames. This test is labeled 'oracle' and 'phase5'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_p_qcif_10f
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/p_qcif_10f.265 176 144
921650ab84b8e366a1aa4e16f2527e46 $)
set_tests_properties(oracle_p_qcif_10f PROPERTIES LABELS "oracle;phase5"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Initialize dash.js Player with HEVC Support
Source: https://github.com/privaloops/hevc.js/blob/main/site/docs/dashjs-plugin.html
Import the attachHevcSupport function and call it on your dash.js player instance to enable HEVC playback.
```javascript
import dashjs from 'dashjs';
import { attachHevcSupport } from '@hevcjs/dashjs-plugin';
const video = document.querySelector('video');
const player = dashjs.MediaPlayer().create();
// One line — that's it
attachHevcSupport(player);
player.initialize(video, 'https://example.com/stream/manifest.mpd', true);
```
--------------------------------
### Handle File Drop Events
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Sets up event listeners for drag-and-drop file handling. Includes dragover, dragleave, and drop events.
```javascript
const dropZone = document.getElementById('drop-zone');
dropZone.addEventListener('click', () => document.getElementById('file-input').click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
const file = e.dataTransfer.files[0];
if (file) decodeFile(file);
});
```
--------------------------------
### Initialize WebGL Context
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Initializes the WebGL rendering context. Returns true if successful, false otherwise.
```javascript
function initGL() {
canvas = document.getElementById('canvas');
try {
gl = canvas.getContext('webgl2', {
antialias: false,
depth: false,
alpha: false,
// Use 'webgl' for older browsers
// gl = canvas.getContext('webgl', { antialias: false, depth: false, alpha: false });
});
} catch (e) {
console.error(e);
return false;
}
if (!gl) {
alert('WebGL 2 not supported');
return false;
}
const ext = gl.getExtension('EXT_color_buffer_float');
if (!ext) {
alert('EXT_color_buffer_float not supported');
return false;
}
return true;
}
```
--------------------------------
### Add Oracle Test for Toy Bitstream (QP30)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds a specific oracle test for a toy bitstream with QP30. This test is labeled 'oracle', 'phase4', and 'toy'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_toy_qp30
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/toy_qp30.265 64 64
9f59878470d904dc2e162d3d191611af $)
set_tests_properties(oracle_toy_qp30 PROPERTIES LABELS "oracle;phase4;toy"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Add Oracle Test for Phase 5 (B-frames, QCIF, 10 frames)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds an oracle test for Phase 5, focusing on B-frames for a QCIF resolution over 10 frames. This test is labeled 'oracle' and 'phase5'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_b_qcif_10f
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/b_qcif_10f.265 176 144
dffe2712c0561c75ee40256d59299826 $)
set_tests_properties(oracle_b_qcif_10f PROPERTIES LABELS "oracle;phase5"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Add Oracle Test for Toy Bitstream (QP10)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds a specific oracle test for a toy bitstream with QP10. This test is labeled 'oracle', 'phase4', and 'toy'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_toy_qp10
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/toy_qp10.265 64 64
ff469ce7e872152490b9b39b0d05dd7b $)
set_tests_properties(oracle_toy_qp10 PROPERTIES LABELS "oracle;phase4;toy"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Add Oracle Test for Phase 4 (I-frame, 64x64, QP22)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds an oracle test for Phase 4, specifically an I-frame of size 64x64 with QP22. This test is labeled 'oracle' and 'phase4'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_i_64x64_qp22
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/i_64x64_qp22.265 64 64
48c64cd2de381113913149e92065d66c $)
set_tests_properties(oracle_i_64x64_qp22 PROPERTIES LABELS "oracle;phase4"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Native Debug Build with Tests
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Instructions for building the decoder in debug mode with tests enabled using CMake. This is useful for development and verification.
```bash
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
cd build && ctest --output-on-failure # 128 tests
```
--------------------------------
### Attach, Load, and Play Media
Source: https://github.com/privaloops/hevc.js/blob/main/demo/shaka.html
Attaches the player to the video element, loads the specified media URL, and attempts to play the media. Includes error handling for loading and a catch for potential autoplay blocking.
```javascript
try {
console.log('[demo] player.attach()');
await player.attach(video);
console.log('[demo] player.load()', url);
await player.load(url);
video.play().catch((e) => console.warn('[demo] video.play() blocked', e?.message));
document.getElementById('status').textContent = 'Loaded — playback should start';
} catch (e) {
console.error('[demo] load error', e);
document.getElementById('status').textContent = 'Load error: ' + (e.message || JSON.stringify(e));
}
```
--------------------------------
### Configure Oracle Conformance Tests
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
This section configures 'oracle' conformance tests that compare the decoder's YUV output MD5 against a reference. These tests are designed to be run separately using `ctest -L oracle` and will skip gracefully if the decoder cannot produce output for a specific feature.
```cmake
set(FIXTURES_DIR "${CMAKE_SOURCE_DIR}/tests/conformance/fixtures")
set(ORACLE_SCRIPT "${CMAKE_SOURCE_DIR}/tools/oracle_test.sh")
# All oracle tests skip gracefully when decoder can't produce output yet
# Exit code 2 = SKIP (decoder not implemented for this feature)
```
--------------------------------
### Load HEVC Stream with dash.js and HEVC.js
Source: https://github.com/privaloops/hevc.js/blob/main/site/index.html
Initializes a dash.js player to load and play an HEVC stream, attaching HEVC.js for decoding. This function also sets up event listeners for playback status and errors.
```javascript
let player = null;
async function loadStream() {
const url = '../demo/streams/dash_abr/manifest.mpd';
const video = document.getElementById('player');
player = dashjs.MediaPlayer().create();
await HevcDash.attachHevcSupport(player, {
workerUrl: '../demo/transcode-worker.js',
});
player.on(dashjs.MediaPlayer.events.PLAYBACK_STARTED, () => {
document.getElementById('status').textContent = 'Playing via dash.js + hevc.js';
});
player.on(dashjs.MediaPlayer.events.ERROR, (e) => {
document.getElementById('status').textContent = 'Error: ' + (e.error && e.error.message ? e.error.message : JSON.stringify(e.error));
});
player.initialize(video, url, true);
document.getElementById('status').textContent = 'Loading HEVC stream…';
}
```
--------------------------------
### Add CLI Executable
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Defines the native command-line interface executable for the project. Links against the main library.
```cmake
add_executable(hevc-decode
src/main.cpp
)
target_link_libraries(hevc-decode PRIVATE hevc-decode-lib)
```
--------------------------------
### Configure Compute-aware ABR Settings
Source: https://github.com/privaloops/hevc.js/blob/main/packages/shaka-plugin/README.md
Customize the behavior of the compute-aware Adaptive Bitrate (ABR) by adjusting parameters like target speed and thresholds. This allows for finer control over variant selection based on transcoding performance.
```javascript
// Tune the decider (defaults: measureWindow 2, lowerAfter 1, raiseAfter 6, targetSpeedX 1.3)
registerHevcTransmuxer(shaka, {
adaptiveCompute: { targetSpeedX: 1.5, lowerAfter: 2 },
});
// Telemetry hook — fires per segment, not just on cap changes
handle.attachComputeAware(player, {
onObservation: (stat, avgSpeedX, capIndex, reason) => {
console.log(`speedX=${stat.speedX.toFixed(2)} cap=${capIndex} (${reason})`);
},
});
// Opt out
registerHevcTransmuxer(shaka, { adaptiveCompute: false });
```
--------------------------------
### Add Test for 10-bit Full QCIF 10fps Bitstream
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Defines a test case for a 10-bit Main 10 profile Full QCIF 10fps bitstream using the ORACLE_SCRIPT. This test is part of the phase 7 milestone suite.
```cmake
add_test(NAME oracle_full_qcif_10f_10bit
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/full_qcif_10f_10bit.265 176 144
19efbf36509c6ecb86e42a7f709998b5 $ yuv420p10le)
set_tests_properties(oracle_full_qcif_10f_10bit PROPERTIES LABELS "oracle;phase7;main10;milestone"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Copy Static Assets
Source: https://github.com/privaloops/hevc.js/blob/main/packages/core/README.md
Copy the necessary static files (worker, Emscripten glue code, and WASM binary) from node_modules to your public/static directory. Adjust the destination path as needed for your project structure.
```bash
cp node_modules/@hevcjs/core/dist/transcode-worker.js public/
cp node_modules/@hevcjs/core/dist/wasm/hevc-decode.js public/
cp node_modules/@hevcjs/core/dist/wasm/hevc-decode.wasm public/
```
--------------------------------
### C API Usage for HEVC Decoding
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Demonstrates the basic lifecycle and decoding process using the C API. Ensure the 'wasm/hevc_api.h' header is included.
```c
#include "wasm/hevc_api.h"
HEVCDecoder* dec = hevc_decoder_create();
hevc_decoder_decode(dec, data, size);
int count = hevc_decoder_get_frame_count(dec);
for (int i = 0; i < count; i++) {
HEVCFrame frame;
hevc_decoder_get_frame(dec, i, &frame);
// frame.y / frame.cb / frame.cr — YUV planes (uint16_t*)
// frame.width / frame.height — luma dimensions
// frame.bit_depth — 8 or 10
}
hevc_decoder_destroy(dec);
```
--------------------------------
### Register HEVC Transmuxer and Load Stream
Source: https://github.com/privaloops/hevc.js/blob/main/packages/shaka-plugin/README.md
Import and register the HEVC transmuxer, then attach it to a Shaka Player instance and load a media stream. The compute-aware ABR is enabled by default.
```javascript
import shaka from 'shaka-player';
import { registerHevcTransmuxer } from '@hevcjs/shaka-plugin';
const handle = registerHevcTransmuxer(shaka, {
workerUrl: '/transcode-worker.js',
wasmUrl: '/hevc-decode.js',
});
const player = new shaka.Player();
await player.attach(document.querySelector('video'));
handle.attachComputeAware(player); // compute-aware ABR (on by default)
await player.load('https://example.com/stream/manifest.mpd');
// later: handle(); // unregisters transmuxer AND detaches compute-aware
```
--------------------------------
### Add Test for Big Buck Bunny 1080p 50fps
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Defines a test case for the Big Buck Bunny 1080p 50fps bitstream using the ORACLE_SCRIPT. This test is part of the real-world bitstream suite.
```cmake
add_test(NAME oracle_bbb1080_50f
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/bbb1080_50f.265 1920 1080
a7c12e24701e3636ce58246033bff6ed $)
set_tests_properties(oracle_bbb1080_50f PROPERTIES LABELS "oracle;phase6;realworld"
SKIP_RETURN_CODE 2)
```
--------------------------------
### Initialize Web Worker for Decoding
Source: https://github.com/privaloops/hevc.js/blob/main/demo/index.html
Initializes the Web Worker for HEVC decoding. Sets up message handling for communication with the worker.
```javascript
function initWorker() {
worker = new Worker('worker.js');
worker.onmessage = (e) => {
const msg = e.data;
switch (msg.type) {
case 'ready':
setStatus('Ready — open a .265 file or use the sample clip');
break;
case 'info':
streamInfo = msg.info;
break;
case 'frame':
frames.push(msg.frame);
break;
case 'done':
decodeTimeMs = performance.now() - decodeStartTime;
setStatus(`Decoded ${msg.frameCount} frames in ${(decodeTimeMs / 1000).toFixed(2)}s`);
updateInfo();
if (frames.length > 0) {
currentFrame = 0;
showFrame(0);
document.getElementById('btn-prev').disabled = false;
document.getElementById('btn-next').disabled = false;
document.getElementById('btn-play').disabled = false;
}
break;
case 'error':
setStatus('Error: ' + msg.message);
break;
}
};
worker.postMessage({ type: 'init', wasmUrl: 'hevc-decode.js' });
}
```
--------------------------------
### Enable Adaptive Compute
Source: https://github.com/privaloops/hevc.js/blob/main/site/docs/dashjs-plugin.html
Enables compute-aware adaptive bitrate control by default. This caps dash.js variants when the device cannot sustain transcoding in real-time. It also provides options to tune specific knobs or opt out entirely.
```javascript
// Default — adaptiveCompute is ON, defaults are sensible
const cleanup = await attachHevcSupport(player, {
workerUrl: '/transcode-worker.js',
});
// To tune knobs:
// attachHevcSupport(player, { adaptiveCompute: { targetSpeedX: 1.5 } })
// To opt out:
// attachHevcSupport(player, { adaptiveCompute: false })
player.initialize(videoElement, mpdUrl, true);
// Cleanup detaches the compute-aware listener AND uninstalls MSE intercept
cleanup();
```
--------------------------------
### WebAssembly Build with Emscripten
Source: https://github.com/privaloops/hevc.js/blob/main/README.md
Steps to compile the HEVC decoder to WebAssembly using Emscripten and CMake. This produces a small JS/WASM bundle suitable for web deployment.
```bash
source ~/emsdk/emsdk_env.sh
emcmake cmake -B build-wasm -DBUILD_WASM=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build-wasm
# Output: build-wasm/hevc-decode.js + hevc-decode.wasm (236KB)
```
--------------------------------
### adaptiveCompute
Source: https://github.com/privaloops/hevc.js/blob/main/site/docs/shaka-plugin.html
Enables compute-aware adaptive bitrate (ABR) that caps Shaka variants when the device cannot sustain transcoding at real-time. This feature is enabled by default.
```APIDOC
### adaptiveCompute — compute-aware ABR on by default
Feedback loop that caps Shaka variants when the device can't sustain transcoding at real-time. Shaka's bandwidth-based ABR keeps choosing freely — we only narrow the ceiling it's allowed to pick from. Useful on devices where WASM HEVC decode + WebCodecs H.264 encode runs slower than playback (typical on low-end Windows boxes at 1080p+). **On by default** — pass `adaptiveCompute: false` to opt out.
#### Example Usage
```javascript
// Default — adaptiveCompute is ON, defaults are sensible
const handle = registerHevcTransmuxer(shaka, {
workerUrl: '/transcode-worker.js',
});
// To tune knobs:
// registerHevcTransmuxer(shaka, { adaptiveCompute: { targetSpeedX: 1.5 } })
// To opt out:
// registerHevcTransmuxer(shaka, { adaptiveCompute: false })
const player = new shaka.Player();
const detach = handle.attachComputeAware(player); // after player exists
await player.load(manifestUrl);
// Cleanup
detach(); // stop observing
handle(); // unregister transmuxer + detach
```
#### Tuning knobs
- `targetSpeedX` (number, default 1.3): Required headroom over real-time before the cap is allowed to rise. `1.3` means the rolling average must show transcode at 1.3× real-time before we let the host ABR pick a higher variant.
- `measureWindow` (number, default 2): Rolling-window size for smoothing. Default is intentionally small — the cost of staying too long at an unreachable quality (buffer underrun, frozen playback) is much worse than the cost of a brief unnecessary dip.
- `lowerAfter` (number, default 1): Consecutive windows averaging below 1.0× before lowering the cap one notch. Default `1` — react on the first confirmed low window.
- `raiseAfter` (number, default 6): Consecutive windows averaging above `targetSpeedX` before raising the cap one notch. Wider than `lowerAfter` by design — hysteresis prevents yo-yo between two adjacent variants.
- `onObservation` ((stat, avg, cap) => void): Optional telemetry sink called for every segment (not just cap changes). Handy for plotting `speedX` over time in a demo or admin panel.
Caps are applied via Shaka's public `player.configure({ abr: { restrictions: { maxHeight, maxBandwidth } } })` — no fork of the ABR controller, no reach into private APIs.
```
--------------------------------
### Configure Compute-aware ABR for HEVC Plugin
Source: https://github.com/privaloops/hevc.js/blob/main/packages/dashjs-plugin/README.md
Customize the adaptive compute settings for the HEVC plugin. This includes adjusting parameters like `targetSpeedX` and `lowerAfter`, or providing a telemetry hook for observation.
```javascript
// Tune (defaults: measureWindow 2, lowerAfter 1, raiseAfter 6, targetSpeedX 1.3)
await attachHevcSupport(player, {
adaptiveCompute: { targetSpeedX: 1.5, lowerAfter: 2 },
});
// Telemetry hook — fires per segment, not just on cap changes
await attachHevcSupport(player, {
adaptiveCompute: {
onObservation: (stat, avgSpeedX, capIndex, reason) => {
console.log(`speedX=${stat.speedX.toFixed(2)} cap=${capIndex} (${reason})`);
},
},
});
// Opt out
await attachHevcSupport(player, { adaptiveCompute: false });
```
--------------------------------
### Initialize and Transcode Media Segments with SegmentTranscoder
Source: https://github.com/privaloops/hevc.js/blob/main/site/docs/shaka-plugin.html
This snippet demonstrates how to use the SegmentTranscoder from '@hevcjs/core' for advanced, direct transcoding. It requires initializing the transcoder, preparing an init segment, and then processing media segments.
```javascript
import {
SegmentTranscoder
} from '@hevcjs/core';
const transcoder = new SegmentTranscoder({ fps: 25 });
await transcoder.init();
// Synthesize a matching H.264 init segment up-front
const init = await transcoder.prepareInit(hevcInitBytes);
// Then transcode media segments
const h264Segment = await transcoder.processMediaSegment(mediaSegmentBytes);
```
--------------------------------
### Add Oracle Test for Phase 6 (Full Pipeline, Milestone)
Source: https://github.com/privaloops/hevc.js/blob/main/CMakeLists.txt
Adds an oracle test for Phase 6, representing the full pipeline with B-frames, deblocking, and SAO, considered a Main profile milestone. This test is labeled 'oracle', 'phase6', and 'milestone'. It will skip if the decoder is not yet implemented for this feature.
```cmake
add_test(NAME oracle_full_qcif_10f
COMMAND ${ORACLE_SCRIPT} ${FIXTURES_DIR}/full_qcif_10f.265 176 144
a4984ee61d18ac9619808c5338132eaa $)
set_tests_properties(oracle_full_qcif_10f PROPERTIES LABELS "oracle;phase6;milestone"
SKIP_RETURN_CODE 2)
```
--------------------------------
### WASM Build
Source: https://github.com/privaloops/hevc.js/blob/main/CONTRIBUTING.md
Builds the project for WebAssembly (WASM) targets using Emscripten. This is necessary for browser-based execution.
```bash
# WASM build
source ~/emsdk/emsdk_env.sh
emcmake cmake -B build-wasm -DBUILD_WASM=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build-wasm
```