Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

whipwhep

Two browser apps for dev/testing WebRTC ingest and playback:

  • Publisher (src/publish/) — sends video/audio to a WHIP endpoint
  • Viewer (src/view/) — plays video/audio from a WHEP endpoint

Plain JS, no bundler, no deps. Each app is a tiny Node HTTP server (no framework) plus a static HTML/JS/CSS page.

Run

npm start                 # both: publisher on :8080, viewer on :8081
npm run publish           # publisher only
npm run view              # viewer only

Open http://localhost:8080/ (publisher) and http://localhost:8081/ (viewer). URLs are printed on startup.

Server files use node --watch, so editing server.js auto-restarts. Client files (HTML/JS/CSS) are served off disk per request — just refresh the browser.

Config

Each app has its own config.jsonc (JSONC — // comments and trailing commas are fine; gitignored; see config.example.jsonc for the schema):

  • src/publish/config.jsoncport, urls (WHIP endpoints), files (named mp4 paths — absolute, ~-expanded, or relative to the repo root), noisePresets
  • src/view/config.jsoncport, urls (WHEP endpoints)

The first URL in urls is preselected on page load. Re-reads on every page load — no restart needed.

Publisher features

WHIP publisher UI

  • Source: animated test pattern, camera + mic, or any mp4 file from config (looped). Test pattern uses setInterval (not rAF) so it keeps painting in background tabs.
  • Codec: H264 / VP8 / VP9. "exclusive" checkbox uses strict setCodecPreferences filter; unchecked = reorder with fallback.
  • Resolution / bitrate: target res via scaleResolutionDownBy + maxBitrate via setParameters.
  • Noise tuning (test pattern only): live sliders for count / size / intensity, seeded from config.noisePresets. Forces the encoder to actually consume the bitrate budget.
  • mute audio (right of Stop): mutes the local preview <video> (does not affect what's sent over WebRTC).
  • mute mic (right of Source): default ticked. Toggles audioTrack.enabled on the camera+mic source — sends silence over the track, keeps connection alive. No effect on test-pattern / mp4 sources.

Viewer features

WHEP viewer UI

  • Receive-only <video> + <audio> transceivers. pc.ontrack adds tracks to a single MediaStream (some WHEP servers deliver each track in its own stream, so we can't just use ev.streams[0]).
  • mute audio: default ticked.

Debugging

Both apps log to a scrollable Log panel with copy / clear buttons. Cleared on each new session. The activity log shows:

  • Local ICE candidates as gathered (host and srflx — STUN provides the latter)
  • ICE / PC state transitions
  • SDP offer/answer (collapsible, with their own copy/clear)
  • Every 5s: codec, bytes/packets sent or received, frames encoded/decoded, NACK/PLI/retx counts, selected ICE candidate pair (types, addresses, RTT)
  • Viewer also logs <video> element events (playing, waiting, stalled, error, etc.) and per-track mute/unmute/ended

Status line shows: target → actual res/bitrate, fps, codec, quality-limit reason, ICE/PC state.

STUN

Both apps configure STUN servers (stun.cloudflare.com:3478, stun.l.google.com:19302). Without STUN the offer only carries host (LAN) candidates, which public WHIP/WHEP servers can't reach.

ICE gathering is capped at 2s (Chrome can spend ~40s otherwise waiting on TCP/IPv6 probes to time out). The log line offer ready (ice-gather: timeout, gathering) is expected and harmless.

A/V sync limitation (mp4 source)

Publishing an mp4 file uses HTMLMediaElement.captureStream(), which the WebRTC spec explicitly permits to lose audio/video sync. It can vary run-to-run (audio leads or lags by tens to a few hundred ms) and degrades within a session as the browser's video encoder stalls under load (worse at 1080p). The local preview <video> stays in sync so it looks fine in the publishing browser, but the published RTP is offset.

  • For sync-faithful capture, publish with a real encoder (OBS, etc.), not the mp4 source.
  • Only the mp4 file source is affected: the test-pattern source makes audio via Web Audio and the camera source uses getUserMedia, so neither goes through media-element captureStream.

License

MIT — see LICENSE.

About

Browser WHIP publisher + WHEP viewer for WebRTC dev/testing

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages