# Browser client MVP

This patch adds a small embedded browser client:

```sh
./build/hyprtile-remoteviewer host \
  --output HEADLESS-1 \
  --fps 30 \
  --bitrate 2500 \
  --web-port 7780 \
  --control-output HEADLESS-1
```

Open:

```text
http://127.0.0.1:7780/
```

The browser client uses:

- `WebSocket /video` for the IVF/VP9 byte stream
- WebCodecs `VideoDecoder` for VP9 decode
- Canvas rendering
- `WebSocket /input` for mouse/keyboard JSON messages
- optional `WebSocket /audio` for low-latency PCM audio when started with `--audio`

## Important limitation

WebCodecs is generally only available in secure contexts. `localhost` is usually fine. For LAN testing with `http://HOST:7780`, Chromium may require:

```sh
chromium --unsafely-treat-insecure-origin-as-secure=http://HOST:7780 http://HOST:7780/
```

A production version should serve HTTPS or move to WebRTC.

## Audio and AV modes

For interactive remote control with sound, start the host with `--audio`. This
keeps the low-latency `/video` WebSocket path and adds a separate `/audio`
WebSocket. This is the recommended browser-client mode.

`--av` is an experimental watch/media mode. It serves a muxed WebM+Opus stream
via `/avstream.webm` and lets the browser media pipeline handle A/V sync. That
adds noticeable buffering and should not be used for realtime desktop control.

## Current input support

The browser sends:

```json
{"t":"move","x":0.5,"y":0.5}
{"t":"button","button":1,"down":true}
{"t":"wheel","dx":0,"dy":120}
{"t":"key","code":"KeyA","down":true}
```

Pointer and keyboard events can be injected when `--control-output` is used.

The browser client supports a dedicated remote-control mode:

- double-click the canvas, click **Fullscreen/Lock**, or press `Ctrl+Alt+F` to request browser fullscreen and Pointer Lock
- while Pointer Lock is active, the local mouse cursor is hidden and relative mouse movement is converted into absolute coordinates for the dedicated output
- `Esc` leaves Pointer Lock/fullscreen using the browser's native escape behavior
- the client maps letterboxed `object-fit: contain` coordinates correctly, so fullscreen and non-fullscreen modes should not produce an offset cursor

Keyboard injection uses `zwp_virtual_keyboard_v1` when the compositor exposes it.

## Why this is still a stepping stone

This is not final WebRTC yet. It is a direct browser MVP that proves:

```text
wlroots capture -> VP9/libvpx -> WebSocket -> WebCodecs -> Canvas
browser input -> WebSocket -> host input dispatcher
```

The final low-latency transport should be WebRTC/RTP.

## Video troubleshooting

If the browser shows a black canvas but input events reach the host, the HTTP/WebSocket layer is working and the problem is almost always in the WebCodecs VP9 decode path.

The browser client now:

- parses IVF headers and per-frame packet headers,
- waits for the first VP9 keyframe after connecting,
- marks VP9 chunks as `key` or `delta`,
- shows `dropped pre-key` and decoder queue stats in the top bar.

Use Chromium/Chrome for this MVP. Firefox may not expose the same WebCodecs VP9 decoder behavior.


## Binding the browser client

Use `--web-bind 0.0.0.0 --web-port 7780` to serve the browser client on all interfaces. Open `http://HOST_IP:7780/` from another device. If `--web-bind` is omitted, it defaults to `--bind`; if `--bind` is omitted, it defaults to `127.0.0.1`.

## Authentication

Add `--auth-password SECRET` or `--auth-keyfile FILE` to protect the browser
WebSocket endpoints `/video` and `/input`. The bundled web client prompts for
the secret and then connects with that token. Prefer HTTPS/WSS for any non-local
use so the token is not sent in clear text.
