Broadcast Stream payload and browser viewer for PlayStation 5 — with a PS4 backend in the same tree.
Overview · Features · Supported firmware · Usage · Build · HTTP API · Limits · Docs
The console program is split by responsibility under payload/src/common/ and
payload/src/platform/{ps5,ps4}/, with the offset tables of each firmware kept
next to the code that selects them (platform/*/offset/). Measurement tools
(dump payloads, generators, ledgers) live in tools/ and are deliberately
outside the capture path. Generated assets stay in payload/build/generated/.
The browser viewer is embedded into the payload at build time, so a single ELF
carries capture, HTTP transport and UI. See
docs/ARCHITECTURE.md for the dependency rules and
CHANGELOG.md for the release history.
PBS Payload runs on the console, captures the live video stream and serves it to a browser over the local network: MJPEG frames, RTP packetized H.264, or a recorded file written on the console itself. The same viewer starts, stops and re-profiles the capture without reloading the payload.
Everything the payload does to firmware memory is derived from a memory dump of that exact firmware, never from a neighbouring one. A profile is a table of instruction addresses — the Avcap2 request geometry and the RemotePlay constructor — and a wrong address is not a soft failure, so an unknown build is refused by design.
Design rules
├── Truth over guesses → a profile exists only if it was dumped and disassembled
├── Fail closed → unknown request size = refused, never borrowed
├── Measured claims → docs say what was verified on hardware, not what might work
├── Small surface → one HTTP server, one embedded viewer, no external runtime
└── Extras are opt-in → branding, dumps and diagnostics are build switches
| Area | What it gives you |
|---|---|
| Capture | Hardware H.264 encoder via Avcap2; 720p / 1080p / 1440p / 2160p profiles, 60 fps mode |
| Profiles | Switching at runtime through /capture/profile; PS5 auto-selects 1080p for an observed 1080p output mode |
| Audio | Stereo 48 kHz float32 PCM (/audio) |
| Transport | HTTP MJPEG (/frames), RTP packetization for external players, capture restart and discontinuity handling |
| Viewer | Built-in browser UI: live stats, resolution switching, forced keyframe, recording, download |
| Recording | Writes an H.264 file on the console, served back from /recordings/ |
| Firmware | 24 measured PS5 profiles plus audited family ranges; PS4 runtime layout discovery |
| Diagnostics | Offset, dependency and module dump payloads that produce the next firmware profile |
| Reliability | Port fallback (ten candidates), RTP backlog resync with keyframe request, periodic keyframe refresh |
The default requested encoder budget is 60 Mbit/s; the configurable range is 1–100 Mbit/s. Actual output depends on scene and encoder — a higher budget is not a guarantee of higher measured bitrate.
Each row was disassembled from a dump of that firmware: the request geometry from
libSceAvcap2.sprx.prefix.bin, the RemotePlay constructor from the
SceRemotePlay EBOOT. The firmware ID is 0xMMMMmmmm (major, minor, build).
| Release | Firmware ID | Request | kind |
|---|---|---|---|
| 3.20 | 0x03200004 |
0x218 |
0xe8 |
| 4.00 | 0x04000042 |
0x230 |
0x100 |
| 4.51 | 0x04510001 |
0x230 |
0x100 |
| 5.00 | 0x05000040 |
0x298 |
0x168 |
| 5.10 | 0x05100023 |
0x298 |
0x168 |
| 5.50 | 0x05500008 |
0x298 |
0x168 |
| 6.02 | 0x06020004 |
0x2a8 |
0x168 |
| 7.20 | 0x07200005 |
0x2a8 |
0x168 |
| 8.40 | 0x08400005 |
0x2a8 |
0x168 |
| 9.20 | 0x09200005 |
0x2b0 |
0x168 |
| 9.40 | 0x09400008 |
0x2b0 |
0x168 |
| 9.60 | 0x09600004 |
0x2b0 |
0x168 |
| 10.01 | 0x10010000 |
0x2b8 |
0x188 |
| 10.60 | 0x10600006 |
0x2b8 |
0x188 |
| 11.20 | 0x11200005 |
0x2e0 |
0x1a0 |
| 11.40 | 0x11400005 |
0x2e0 |
0x1a0 |
| 11.60 | 0x11600005 |
0x2e0 |
0x1a0 |
| 12.00 | 0x12000043 |
0x2e0 |
0x1a0 |
| 12.40 | 0x12400009 |
0x2e0 |
0x1a0 |
| 12.70 | 0x12700001 |
0x2e0 |
0x1a0 |
| 13.00 | 0x13000040 |
0x2e0 |
0x1a0 |
| 13.20 | 0x13200006 |
0x2e0 |
0x1a0 |
| 13.42 | 0x13420000 |
0x2e8 |
0x1a8 |
| 13.60 | 0x13600007 |
0x2e8 |
0x1a8 |
Ten of them are exact-only (3.20, 4.00, 4.51, 5.00, 5.10, 5.50, 9.60, 11.20, 13.00, 13.20): another build revision of the same release is refused instead of being served with a geometry it was never measured on.
Between measured anchors, releases whose Avcap2 geometry is unchanged are accepted without their own profile:
| Family range | Anchor profile | Releases accepted |
|---|---|---|
| 6.02 – 8.40 | 602 |
6.50, 7.00, 7.01, 7.40, 7.60, 7.61, 8.00, 8.20 |
| 9.20 – 9.40 | 920 |
— |
| 10.01 – 10.60 | 1001 |
10.20, 10.40 |
| 11.40 – 12.70 | 1160 |
12.02, 12.20, 12.60 |
| 13.40 – 13.60 | 1360 |
13.40 |
A range stands in only for releases inside the audited window; it never covers a release listed as exact-only, and never covers another build of an exact-only release.
| Release | Why it is refused | What it needs |
|---|---|---|
| 9.00 | lies outside the 9.20–9.40 family | dump of libSceAvcap2 + SceRemotePlay |
| 11.00 | sits between the 10.01–10.60 geometry (0x2b8) and the measured 11.20 build (0x2e0) |
dump |
| 14.00 | beyond the last audited family (13.40–13.60) | dump |
| 14.10 | beyond the last audited family (13.40–13.60) | dump |
If your console reports one of these, the payload refuses to patch rather than risk a wrong request size. Run the dump build and open an issue with the archive — that is exactly how 3.20, 4.00, 4.51, 5.10, 11.20, 13.00, 13.20, 13.42 and the other profiles were added.
| Release | State |
|---|---|
| 14.00 | validated on hardware (fw 0x14008001): kernel layout discovered at runtime (acmgr +0x3b3830, gates +0x3673f5/+0x3678e1), gates armed, ptrace identity adopted, Avcap Init/Open/Start all 0x0, 720p worker backend streaming over HTTP |
| 11.00 | validated on hardware: 720p worker backend, ~59.9 fps over 180 s sessions, video read through exported sceVshAvcapReadVideo |
| 9.00 | kernel layout (acmgr + both gates) discovered at runtime; Avcap exports measured; capture internals not validated |
| 6.72 | offset report and diagnostics only |
PS4 kernel offsets do not transfer between releases, so a build without a
matching layout is refused before any patch is applied. PS4 audio is disabled.
Deeper detail: docs/PS4_FIRMWARE_OFFSETS.txt,
docs/PS4_HANDOFF.md.
Load pbs.elf with your payload loader. With no arguments the stream starts on
port 9999 at 2560×1440 on modern profiles. The measured 3.20, 4.00, 4.51 and
5.10 builds start at 1920×1080 and offer 720p, 1080p and 2160p: their native
resolution table has no 1440p entry.
pbs.elf avserve [720p|1080p|1440p|2160p] [bitrate=N] [minqp=N] [norate]
[offsetdump] [dependencydump] [moduledump] [noaudio] [nocontroller] [port]
Open http://<console-ip>:9999/ in a browser on the same network. Audio starts
after a user gesture (browser autoplay policy). If the port is already taken by
another process, the payload binds the next free port in a ten-port window and
says which one in the console notification.
Run the payload as a background service on the console; it keeps capturing while
the viewer is closed. POST /stop releases the capture resources.
Requires LLVM (clang + lld) and a payload SDK:
| Target | SDK | Output |
|---|---|---|
| PS5 | ps5-payload-dev/sdk |
payload/build/ps5/pbs.elf |
| PS4 | ps4-payload-dev/sdk |
payload/build/ps4/pbs-remote-stream.elf |
| PS5 dump | same PS5 SDK | payload/build/pbs-ps5-dump.elf |
# Linux / WSL
export PS5_PAYLOAD_SDK=$HOME/ps5-payload-sdk
./payload/build.sh ps5# Windows (WSL-backed)
./payload/build.ps1 -Platform PS5 -SdkPath '~/ps5-payload-sdk'Optional switches: -WebLogo (PBS_WEB_LOGO=1 in WSL) embeds the owner banner,
-Kind dump builds the measurement payload, -Ps5DefaultResolution changes the
initial profile. Offline regression runs on the host with
tools/test_ps4_host.sh and in CI through verify.yml. Full instructions:
BUILDING.md, release procedure: docs/RELEASING.md.
| Endpoint | Method | Purpose |
|---|---|---|
/ |
GET | Embedded viewer |
/config.json |
GET | Active profile, advertised resolutions, decoder headers |
/status.json |
GET | Width, height, fps, bitrate, recording state |
/frames |
GET | MJPEG stream |
/audio |
GET | Raw PCM (stereo, 48 kHz, float32) |
/capture/profile |
POST | Queue a resolution change (?height=1080) |
/capture/restart |
POST | Restart the capture pipeline |
/video/keyframe |
POST | Force an IDR for the current viewer |
/record/start, /record/stop |
POST | Write or stop a recording on the console |
/recordings, /recordings/<name> |
GET | List and download recordings |
/stop |
POST | Release the capture resources |
/banner.png |
GET | Owner banner, present only in a build made with PBS_WEB_LOGO=1 |
These endpoints are unauthenticated local-network services. Use a trusted LAN and never expose the console to the Internet.
The embedded viewer provides Connect controller for the PS5 firmware profiles already supported by the payload. Connect explicitly, then choose a PS5 user if the console asks. Click the controller area to use its displayed keyboard bindings, use a standard browser gamepad, or hold the right mouse button over the video to move the right stick. Only one viewer owns a controller at a time. Disconnect it with Disconnect controller before leaving the session.
Streaming and controller input are included in the same pbs.elf by default.
Pass nocontroller at launch (pbs.elf avserve nocontroller) to disable input
for that session. To exclude the native controller backend at build time, use
-Ps5Controller 0 with payload/build.ps1, or PBS_PS5_CONTROLLER=0 with
payload/build.sh ps5. The default is 1 in both build scripts. With input disabled,
the viewer hides the controller section and registration is unavailable.
Input is released after 250 ms without updates; after two seconds the viewer session is released, but the virtual device stays registered for the payload lifetime. Reconnecting reuses that device instead of creating another one. Losing focus sends neutral input. Video profile changes keep the controller session. The device and process privilege are removed only when the payload stops. Basic buttons, sticks and triggers are implemented; touch coordinates, motion and feedback are not. This feature is PS5 only.
The backend resolves native libScePad exports locally and uses the existing
firmware selector. Offline tests cover all 24 measured profile IDs, framing,
session ownership, browser focus loss and cleanup failures. Hardware validation
on each firmware remains pending. API availability is checked when connecting;
failure stops the operation. The diagnostic log is /data/pbs/controller.log.
For viewer integrations, GET /input/status reports availability and ownership
without exposing a session token. POST /input/connect returns a token; send it
in X-PBS-Controller for /input/state and /input/disconnect. All three POST
routes require X-PBS-Input: 1. State bodies contain eight unsigned decimal CSV
fields: sequence,buttons,lx,ly,rx,ry,l2,r2. Stick centres are 128, axes and
triggers range from 0 to 255, and sequence numbers must advance modulo 2^32.
Keep sending state at least every 100 ms while connected. Deletion happens only
at payload shutdown; if it fails, cleanup_required and last_rc report it in
status and the log.
- Hardware validation is uneven by design. PS4 11.00 at 720p is validated on real hardware (180 s sessions, ~59.9 fps, decoded recording). PS5 profiles are verified by offline disassembly and bounds checks; the console smoke test is the last step per release and some profiles have not had it yet.
- No native 4K, 120 fps or zero-latency claims. 2160p is a selectable capture profile, not a promise about the encoder or the link.
- PS4 audio is disabled. PS4 capture is validated on 11.00 and 14.00 at 720p; other PS4 firmwares remain unverified.
- Firmware outside the tables above is refused, on purpose.
- The desktop/mobile application is a separate project and is not part of this repository or its license grant.
| Document | Content |
|---|---|
BUILDING.md |
Toolchain, SDK, every build switch |
CHANGELOG.md |
Release history |
docs/PS5_FIRMWARE_OFFSETS.txt |
Per-release geometry, source dump and evidence |
docs/PS5_FIRMWARE_RANGES.md |
Why a release is served by a range, and the bounds |
docs/PS5_OFFSET_PROFILES.md |
How a dump becomes a profile |
docs/STREAM_PROFILES.md |
Runtime resolution switching |
docs/PS4_HANDOFF.md |
PS4 hardware evidence and open points |
docs/ARCHITECTURE.md |
Module boundaries and dependency rules |
docs/RELEASING.md |
Release workflow, store registration, SDK provisioning |
If PBS Payload is useful to you and you want to support continued work:
- John Törnblom — PS5 payload SDK and the
ptraceprocess-call technique this implementation references (prospero-menu/libNineS). - ps4-payload-dev — PS4 payload SDK and reference tooling.
- MemDBG — process memory reference.
- The PlayStation homebrew community — testers, dump contributors and feedback; every new firmware profile in this repository comes from a community dump.
Source code, including the embedded viewer, is licensed GPL-3.0-or-later — see LICENSE. Existing attribution is retained.
payload/web/logo.png (bright, for dark backgrounds) and payload/web/logo-dark.png
(the same mark, inverted for light backgrounds) are PBS branding, not GPL-licensed
artwork: they are included for use with this project, and unrelated reuse needs the
owner's permission. The README picks the right one with GitHub's
#gh-dark-mode-only / #gh-light-mode-only fragments.
payload/web/banner.png is the stream overlay served at /banner.png: it is
embedded only when the build asks for it (PBS_WEB_LOGO), it is not GPL-licensed
either, and a default build does not contain it.
Released under the GNU General Public License v3.0 or later