Skip to content

API Docs ​

For playback failures, MP4 moov/Range, autoplay, CORS and WASM diagnostics, see QA WIKI · Frequently Asked Questions.

This page is the compact reference for the current h265web.js PRO browser SDK.

Need a page you can copy and run? Choose VR360 video, WebRTC playback, live HTTP-FLV/HLS, or MP4 on demand.

Path Rules ​

The current SDK accepts these path styles for both SDK resources and media URLs:

  • Full URL: https://example.com/output/h265web_wasm.js
  • Site-absolute path: /output/h265web_wasm.js
  • Relative path: ./output/h265web_wasm.js

If you want to resolve the SDK resource files from one shared base path, use base_url.

Build Flow ​

Recommended public usage:

  1. Create the player object.
  2. Bind the callbacks you need.
  3. Call build(config).
  4. Call load_media(mediaUrl).
js
const ylplayer = H265webjsPlayer();

ylplayer.on_ready_show_done_callback = function () {
  console.log('ready');
};

ylplayer.video_probe_callback = function (mediaInfo) {
  console.log('probe', mediaInfo);
};

ylplayer.build({
  player_id: 'canvas111',
  base_url: './output/',
  wasm_js_uri: 'h265web_wasm.js',
  wasm_wasm_uri: 'h265web_wasm.wasm',
  width: '100%',
  height: 480,
  auto_play: true,
  ignore_audio: false
});

ylplayer.load_media('./resource/demo.mp4');

build(config) ​

Config Fields ​

ParameterTypeRequiredDescription
player_idstringYesPlayer container element id
wasm_js_uristringYesResource path for h265web_wasm.js
wasm_wasm_uristringYesResource path for h265web_wasm.wasm
base_urlstringNoOptional shared base path for SDK resource resolution
widthnumber | stringNoPlayer width
heightnumber | stringNoPlayer height
colorstringNoPlayer background color
auto_playbooleanNoAuto play after media is ready
readframe_multi_timesnumberNoDemux feed scale
corestringNoExplicit core: mse_hevc, wasm_hevc, webcodec_hevc, webrtc
hls_strategystringNoHLS route policy: auto (default) or mainline; both use the mainline cores
ignore_audiobooleanNoSkip the audio pipeline entirely; this is not mute
format_typestringNoOptional format hint; use 'webm' for extensionless WebM endpoints
media_uristringNoOptional media URL. Public examples still recommend build() followed by load_media()
projection'flat' | 'erp360'NoPlayback view: ordinary flat video flat (default) or VR360 erp360; does not change the codec or decoder
vrobjectNoOptional VR360 starting view and mouse/touch controls: {yaw, pitch, fov, mouse_control, touch_control}

WebM, video-only FLV and PyMKUI ​

Set format_type: 'webm' when a WebM playback endpoint has no .webm suffix. WebM uses the native video path with Auto/MSE selection and a compatible callback contract. Unsupported browser decoding, software-core preferences or incompatible callbacks report WEBM_NATIVE_UNSUPPORTED; native playback errors do not enter the VP8/VP9 software path. This routing does not add VP8/VP9 WASM decoders.

For FLV, missing audio metadata is not treated as proof that an audio track exists. ignore_audio: true skips the audio pipeline; use set_voice(0) to mute while keeping it enabled. HTTP-FLV/WebCodec AAC startup aligns queued audio after the first displayed frame and AudioContext activation. Other protocols and decoders keep their existing startup behavior.

The PyMKUI fork maps RTMP rows to HTTP-FLV and TS rows to HTTP-TS, with fresh on_play authorization on every attempt. HEVC MPEG-TS HLS uses the SDK; AVC native HLS falls back once to SDK MSE only on terminal media errors 3/4. Single MP4/WebM recordings provide flat/ERP view switching and playback controls. ERP must be selected for a real panorama; it is not inferred from image dimensions. HEVC HLS-fMP4 is not implemented by this integration.

VR360 Video Playback ​

To play a 360° video or live stream with a movable view, use a VR360 source and explicitly select erp360. erp360 is the SDK configuration value for viewing a panorama by direction; without it, the video plays flat. You can switch views during playback without changing the media URL or decoder.

Which videos work with VR360? ​

The source must be a monoscopic equirectangular panorama (ERP) covering the full horizontal circle and the directions above and below. A 2:1 image is typical. Setting erp360 on an ordinary flat video does not turn it into a panorama. The SDK does not enable VR360 automatically from a filename, resolution or metadata.

An anamorphic ERP source such as 1920×1080 with SAR 9:8 also works: the full visible image is used, excluding coded padding. Cubemap, cylindrical panoramas, stereo VR, WebXR and gyroscope controls are not supported here.

  • flat: show the entire image like an ordinary video.
  • erp360: show one movable perspective view within the panorama; the media file is not rewritten.

How do I enable VR360 and move the view? ​

Add projection: 'erp360' to the existing build() configuration, then call load_media() as usual:

The current public SDK and the website's pinned CDN build include these VR360 APIs. The demo starts muted and flat; select VR360 explicitly. If an older cached SDK does not have these APIs, update the bundle.

js
ylplayer.on_error_callback = function (error) {
  if (error.domain === 'vr' && error.fatal === false) {
    console.warn('VR fallback:', error.code, error.fallback);
    return; // Do not reload the media or switch decoders for a VR-only error.
  }
  console.error('Playback error:', error);
};
ylplayer.build({
  player_id: 'canvas111',
  base_url: './output/',
  wasm_js_uri: 'h265web_wasm.js',
  wasm_wasm_uri: 'h265web_wasm.wasm',
  width: '100%', height: 480, auto_play: true,
  projection: 'erp360',
  vr: {yaw: 0, pitch: 0, fov: 75, mouse_control: true, touch_control: true}
});
ylplayer.set_voice(0); // Muted output; this does not disable the audio pipeline.
ylplayer.load_media('./your-mono-erp.mp4');

These APIs switch back to flat video, move the view or reset it while playing or paused:

js
ylplayer.set_projection('flat');
ylplayer.set_projection('erp360');
ylplayer.set_vr_view({yaw: 90, pitch: 10, fov: 75});
ylplayer.rotate_vr_view({yaw: 5});
const view = ylplayer.get_vr_view(); // {yaw, pitch, fov, projection}, copied state
ylplayer.reset_vr_view();
ylplayer.set_vr_controls({mouse: true, touch: false});
Field / methodMeaning
yawDegrees, positive turns right, normalized to [-180,180); default 0
pitchDegrees, positive looks up, clamped to [-85,85]; default 0
fovVertical field of view in degrees, clamped to [30,120]; default 75
set_vr_view / rotate_vr_viewAccept a nonempty subset of yaw/pitch/fov, finite numbers only; rotate uses deltas. Flat mode also stores the view
set_vr_controlsNonempty partial update of {mouse,touch}, boolean values only; both default off
All settersReturn true when parameters are accepted, not when the VR picture is already visible. Invalid parameters, before build or after release: false, no state change
get_vr_viewReturns the requested view mode; if VR360 has fallen back to flat, check the error callback. Before build / after release: null. No new view-change callback
reset_vr_viewRestores the initial build view, without changing projection or controls

Unknown VR keys, numeric strings, NaN and Infinity are invalid. An invalid startup VR configuration reports VR_CONFIG_INVALID and uses default flat/view/controls; valid media configuration can still build. Runtime invalid input simply returns false.

Mouse control enables drag and wheel zoom; touch enables single-pointer drag. Listeners only operate on the active ERP surface and are removed on flat/detach/release. No keyboard listener is installed: applications can call rotate_vr_view() from their own keyboard UI. change_media(), rebuild() and internal core fallback retain current view, initial reset view, requested projection and controls. Full release() destroys them; the next build establishes a new initial view.

Advanced: what happens when switching views or taking a screenshot? ​

Normal flat↔VR360 switches keep the same canvas/context/decoder and use cached shader programs. Native/MSE video continues to own decoding, audio and playback time beneath the VR360 canvas; the player does not use display:none on the video or deliberately copy every frame to the CPU. Turning the view or resizing while paused redraws the current frame without advancing playback time or repeating progress callbacks. screenshot(imageId) keeps its existing API and captures what is actually visible; Worker capture may complete asynchronously. Browser/driver internals are not guaranteed to be zero-copy.

What if the browser cannot display VR360? ​

VR360 changes presentation, not codec support. If VR rendering fails, the player attempts to keep ordinary flat playback and reports a non-fatal VR error through the existing on_error_callback. Media decoding errors still follow the normal playback route.

VR does not expand codec support or change route priority. Native/MSE, mainline HLS, WebCodec, WASM and WebRTC use their existing decode paths. Encoded WebRTC requires the separate Job4 server/frame protocol; an ordinary ZLM native WebRTC endpoint does not provide it. Audio-only playback has no VR surface.

URL-backed native video requested as ERP at build uses crossOrigin='anonymous' before src. The media server must allow that CORS request. If it fails, the same candidate retries once with an ordinary video element and reports VR_SOURCE_CORS while continuing flat if playback succeeds. Only a subsequent ordinary load failure re-enters the normal route error policy. MSE fetch/segment CORS and MediaStream signaling permissions still follow their existing rules.

A cross-origin video already loaded without CORS cannot be made texture-readable by changing its attribute during playback—even if the server now sends permissive headers. A runtime ERP request leaves that video playing flat and reports VR_SOURCE_CORS; it does not automatically reload or seek. Use a same-origin source or enable ERP at initial build with a CORS-enabled URL.

ERP output fills the player viewport, with device pixel ratio capped at 2 and backing size capped by GPU capabilities and the internal pixel budget. Source texture limits are independent: reducing output resolution cannot make an oversized source texture supported. Fullscreen should target the player container; native video-only fullscreen may force flat until exited. Context loss can discard queued GPU frames and briefly interrupt video while a replacement surface takes over; it is not a zero-frame-loss guarantee and does not deliberately rebuild the decoder.

Successful internal fallbacks (e.g. C++→JS GL, WebGL2→WebGL1 or VideoFrame→ImageBitmap) do not emit a public VR error. Final ERP→flat fallback uses the existing on_error_callback at most once per media generation/render attachment, with domain:'vr', fatal:false, projection:'erp360', fallback:'flat', code, stage, message and diagnostic context. Requested projection remains erp360; view changes do not repeatedly retry a failed renderer. An explicit flat→ERP transition permits a new attempt. Flat playback still depends on a usable native/2D renderer and successful media decoding.

CodeMeaning
VR_CONFIG_INVALIDInvalid initial VR options
VR_RENDERER_UNAVAILABLENo usable ERP renderer/surface, or unsupported fullscreen target
VR_SHADER_INITERP shader initialization failed after supported backend attempts
VR_TEXTURE_UPLOADFrame cannot be uploaded for ERP
VR_SOURCE_CORSNative source is not texture-readable, or startup CORS retry
VR_TEXTURE_TOO_LARGESource exceeds GPU texture limits
VR_CONTEXT_LOSTERP context cannot continue

HLS Route Policy ​

hls_strategy selects the mainline HLS routing policy. The SDK no longer loads extension cores:

ConfigurationResult
OmittedUses auto; tries compatible mainline candidates only
hls_strategy: 'auto'Uses the default mainline route selected from browser capabilities, the requested core, and bound callbacks
hls_strategy: 'mainline'Uses mainline candidates only

WebRTC Route ​

The SDK recognizes standard WHEP, the webrtc:// compatibility form and the ZLMediaKit private WebRTC API:

text
http://127.0.0.1/index/api/whep?app=live&stream=test
webrtc://127.0.0.1/live/test
http://127.0.0.1/index/api/webrtc?app=live&stream=test&type=play

URL recognition happens inside the SDK. Omit core for automatic routing, or set core: 'webrtc' to require WebRTC. No signaling selector or second public API is needed.

WHEP is the recommended playback input. WHIP is the corresponding publishing protocol and is not a playback URL. The HTTP(S) request carries SDP signaling; media uses WebRTC ICE/DTLS/SRTP after negotiation.

For ZLMediaKit RTMP-to-WebRTC conversion, configure [protocol] modify_stamp=1 and paced_sender_ms=0. modify_stamp=1 rebuilds and smooths timestamps from the receive clock. In the tested modify_stamp=2 case, decoding continued at the expected rate while Chrome dropped frames that arrived on a late native WebRTC timeline. This is a server timestamp requirement, not a player decode setting.

Native WebRTC is the default compatibility/performance route. It is fixed at 1x, does not support seek, and accepts AV, video-only or audio-only negotiation results. Codecs not accepted by the browser's WebRTC stack require server support for the optional h265web-rtc-encoded/v1 encoded-media protocol before a WebCodecs/WASM route is possible.

Core Methods ​

release() ​

js
ylplayer.release();

load_media(mediaUrl) ​

js
ylplayer.load_media('./resource/hevc_test_moov_set_head_16s.mp4');

change_media(mediaUrl) ​

js
ylplayer.change_media('/resource/another-demo.mp4');

play() ​

js
ylplayer.play();

pause() ​

js
ylplayer.pause();

seek(seconds) ​

js
ylplayer.seek(10);

set_playback_rate(rate) ​

js
ylplayer.set_playback_rate(2.0);

Native WebRTC live playback remains at 1x and returns false for unsupported rate changes.

set_voice(volume) ​

js
ylplayer.set_voice(0.5);

With ignore_audio: false, set_voice(0) silences output and a later positive value restores the selected volume. Native/MSE media elements are also unmuted when a positive volume is applied.

set_mute() ​

js
ylplayer.set_mute();

Use set_mute() / set_voice() for reversible mute controls. If the player was built with ignore_audio: true, the audio track was not initialized and changing volume cannot restore it; rebuild with ignore_audio: false instead.

screenshot(imgId) ​

html
<img id="screenshot" style="width:400px;height:340px;background:#e9e9e9;" />
js
ylplayer.screenshot('screenshot');

next_frame() ​

js
ylplayer.next_frame();

resize(width, height) ​

js
ylplayer.resize(640, 480);

fullScreen() ​

js
ylplayer.fullScreen();

closeFullScreen() ​

js
ylplayer.closeFullScreen();

Required Callbacks ​

on_ready_show_done_callback ​

js
ylplayer.on_ready_show_done_callback = function () {
  console.log('on_ready_show_done_callback');
};

video_probe_callback(mediaInfo) ​

js
ylplayer.video_probe_callback = function (mediaInfo) {
  console.log('video_probe_callback', mediaInfo);
};

Feature Callbacks ​

Callback-aware core routing ​

Bind callbacks before build()/load_media(). HLS, MP4, FLV, and TS routing preserve the requested callback contract instead of silently selecting a core that cannot emit it:

  • Raw NAL/frame/render/queue/GPU/A-V-sync callbacks require a software core.
  • av_sync_callback specifically requires the WebCodec core because the FFmpeg/WASM core does not expose equivalent alignment events.
  • SEI callbacks exclude native HLS, but may use MSE when the stream exposes SEI.
  • Probe, ready, play-time, seek, cache, release, error, and fullscreen callbacks are available on native, MSE, and software mainline cores.
  • If an explicitly requested route cannot satisfy the bound callbacks, the player reports an error rather than dropping those callbacks.
  • In this compact build, non-HLS H.264 TS requires MSE. If software-only callbacks are bound, the player reports CALLBACK_CONTRACT_H264_TS_UNSUPPORTED instead of pretending those callbacks are available.

WebRTC uses the existing callback names; no WebRTC-only callback API was added. Native WebRTC emits real probe, ready, loading/cache-state, play-time, video-render, release, error and fullscreen events. It does not fabricate NAL, decoded YUV, internal texture queue, SEI or A/V alignment data that a native <video> element cannot expose. Those low-level callbacks are emitted only when the optional server-supported encoded WebCodecs/WASM route actually produces the corresponding data.

Timestamp units are stable across mainline cores: on_play_time, seek callbacks, and mediaInfo.duration use seconds; NAL/frame/render/SEI/cache-process timestamps use milliseconds. codec in SEI callbacks is numeric (264 or 265).

video_sei_raw_callback(rawSei, pts, dts, codec) ​

Use this when you need the raw SEI payload from the current playback chain.

js
ylplayer.video_sei_raw_callback = function (rawSei, pts, dts, codec) {
  console.log('video_sei_raw_callback', rawSei, pts, dts, codec);
};

video_sei_text_callback(text, pts, dts, codec) ​

Use this when the current playback chain can decode SEI data into readable text.

js
ylplayer.video_sei_text_callback = function (text, pts, dts, codec) {
  console.log('video_sei_text_callback', text, pts, dts, codec);
};

Low-level software-core callbacks ​

These callbacks require access to demuxed or decoded data, so binding any of them makes HLS select a compatible software core:

js
ylplayer.video_nalu_callback = function (ptsMs, dtsMs) {};
ylplayer.video_frame_callback = function (ptsMs, width, height, cacheSize) {};
ylplayer.audio_frame_callback = function (ptsMs, cacheSize) {};
ylplayer.video_render_callback = function (ptsMs, width, height) {};
ylplayer.audio_render_callback = function (ptsMs) {};
ylplayer.request_pkt_callback = function (videoPacketCount, audioPacketCount) {};
ylplayer.nalu_length_callback = function (length) {};
ylplayer.tex_length_callback = function (length) {};
ylplayer.gpu_info_callback = function (info) {};
ylplayer.av_sync_callback = function (state, detail) {};

av_sync_callback states are audio-slower-too-much, audio-faster-too-much, and aligned.

nalu_length_callback and tex_length_callback are query responses; call get_nalu_len() and get_tex_len() respectively. gpu_info_callback is emitted after gpu_memory_info() and receives the read-only WebGL MAX_TEXTURE_SIZE capability. WebGL does not expose total GPU memory, so this API does not allocate test textures or estimate memory by exhausting the GPU.

Playback And Seek Callbacks ​

on_play_time(pts) ​

js
ylplayer.on_play_time = function (pts) {
  console.log('on_play_time', pts);
};

on_play_finished() ​

js
ylplayer.on_play_finished = function () {
  console.log('on_play_finished');
};

on_seek_start_callback(seekTarget) ​

js
ylplayer.on_seek_start_callback = function (seekTarget) {
  console.log('on_seek_start_callback', seekTarget);
};

on_seek_done_callback(seekTarget) ​

js
ylplayer.on_seek_done_callback = function (seekTarget) {
  console.log('on_seek_done_callback', seekTarget);
};

Both seek callbacks receive the requested target in seconds. on_seek_done_callback fires once when the selected core has completed the seek; software cores wait until the target frame is ready/rendered.

Cache Callbacks ​

on_cache_process_callback(timestamp) ​

js
ylplayer.on_cache_process_callback = function (timestamp) {
  console.log('on_cache_process_callback', timestamp);
};

on_load_caching_callback(data) ​

js
ylplayer.on_load_caching_callback = function (data) {
  console.log('on_load_caching_callback', data);
};

on_finish_cache_callback(data) ​

js
ylplayer.on_finish_cache_callback = function (data) {
  console.log('on_finish_cache_callback', data);
};

Lifecycle And Error Callbacks ​

on_release_done_callback() ​

Fires exactly once after the active core and workers have been released. If a worker does not acknowledge release, the SDK terminates it after the bounded release timeout before firing this callback.

js
ylplayer.on_release_done_callback = function () {
  console.log('released');
};

on_error_callback(error) ​

js
ylplayer.on_error_callback = function (error) {
  console.error('player error', error);
};

For HTTP media handled by the demux Worker, failed responses report {type:'http-transport', code, status, message}. HTTP 401/403/404 are reported without retry; transient failures have bounded retries. A browser CORS failure may expose only status: 0. Other playback routes keep their existing error formats.

Fullscreen callbacks ​

js
ylplayer.on_open_fullscreen = function () {};
ylplayer.on_close_fullscreen = function () {};