StreamerResource¶
StreamerResource
¶
Streamer management — screenshots and OCR for PiKVM.
Source code in src/aiopikvm/resources/streamer.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | |
get_state()
async
¶
Get the current streamer state.
Returns:
| Type | Description |
|---|---|
StreamerState
|
Current streamer subsystem state. |
get_ustreamer_state(*, timeout=None)
async
¶
Read ustreamer's own state, straight from ustreamer.
This is the object
StreamerState.streamer holds: kvmd polls
/state on the streamer socket and relays the result into
GET /api/streamer untouched. Reading it here skips that poll, so
the numbers are the ones ustreamer has right now rather than the ones
kvmd last collected — which is what makes
StreamerStream.clients_stat usable for
watching a stream this client itself opened.
Nothing under /streamer speaks the kvmd envelope on the way out,
so a failure here has no error field to match on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float | None
|
Per-call timeout in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
Streamer
|
The running streamer's state. |
Raises:
| Type | Description |
|---|---|
APIError
|
The streamer process is not running, which nginx reports
as HTTP 502 with a page of its own — it has no upstream socket
to reach. It is not
|
ResponseError
|
The body was not the envelope this endpoint documents, which is what a proxy answering instead of ustreamer looks like. |
Source code in src/aiopikvm/resources/streamer.py
mjpeg(*, key=None, extra_headers=False, zero_data=False, timeout=None)
async
¶
Read the MJPEG stream, one frame at a time.
This is ustreamer's own multipart/x-mixed-replace stream, the one
a browser renders by pointing an <img> at it. kvmd has no
equivalent: GET /api/streamer/snapshot gives one frame per
request, and this gives them as ustreamer encodes them.
The iteration ends when the far end stops sending or says it has
stopped — a multipart body's close delimiter, after which RFC 2046
§5.1.1 leaves nothing to read. ustreamer never sends one, so in
practice this is a loop to be left with a break or cancelled from
outside. The streamer has to be running for there to be anything to
read, and kvmd runs it while at least one session asks for video — so
open a ws() around this, or the stream dies
under this loop.
Two of ustreamer's flags are deliberately missing. advance_headers
sends each part's headers before the frame they describe exists, which
drops Content-Length — and every X-UStreamer-* header with it —
so no parser that finds frames by their declared length can follow it;
it is a Chromium rendering workaround with nothing to offer a client
that reads bytes. dual_final_frames is the same for Safari.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str | None
|
A name for this connection. ustreamer echoes it in
|
None
|
extra_headers
|
bool
|
Ask ustreamer to annotate every part with its
|
False
|
zero_data
|
bool
|
Ask for the part headers with no JPEG payload behind
them, which turns this into a cheap frame-timing feed:
|
False
|
timeout
|
float | Timeout | None
|
Override the request timeout. By default the read timeout is disabled — a stream has no end to wait for — while connect, write and pool keep their client-level values. |
None
|
Yields:
| Type | Description |
|---|---|
AsyncIterator[MJPEGFrame]
|
Each frame, with whatever its part headers said about it. |
Raises:
| Type | Description |
|---|---|
APIError
|
The streamer process is not running (HTTP 502 from
nginx, which has no upstream socket to reach), or the path was
refused. Nothing under |
ResponseError
|
The response was not a multipart stream, or a part
arrived with no |
PiKVMError
|
PiKVM became unreachable, or the connection broke mid-stream. |
Source code in src/aiopikvm/resources/streamer.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
set_params(*, quality=None, desired_fps=None, resolution=None, h264_bitrate=None, h264_gop=None, timeout=None)
async
¶
Change the streamer parameters.
kvmd applies these asynchronously — the call returns once the change
is queued, and StreamerState.applied is
what the running streamer ended up with. Read it back to confirm: a
value outside the device's own limits is accepted with HTTP 200 and
then dropped silently, so only re-reading the state shows what
happened. What is rejected outright is a parameter the device does not
have at all — the ones it has are the keys present in
StreamerState.params.
Asynchronously here means about a second: kvmd holds the batch open
for further writes, then applies it and restarts the streamer, so
video drops for a moment. Until that happens neither params nor
applied moves — both describe the streamer that is still running.
That lag has a sharp edge. kvmd compares each incoming value against the running streamer and queues only what differs, so writing the old value back does not cancel a pending change: it is equal to what is running, so it is dropped, and the pending change lands a moment later. Undoing a write means waiting for it to take and then writing the old value — by which time it differs again.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
quality
|
int | None
|
JPEG quality, 1 to 100. Unsupported on devices with no adjustable encoder. |
None
|
desired_fps
|
int | None
|
Target frame rate, 0 to 120 for kvmd, and within
|
None
|
resolution
|
str | None
|
Capture resolution as |
None
|
h264_bitrate
|
int | None
|
H.264 bitrate in kbps, 25 to 20000 for kvmd, and
within
|
None
|
h264_gop
|
int | None
|
H.264 group-of-pictures size, 0 to 60 for kvmd, and
within |
None
|
timeout
|
float | None
|
Per-call timeout in seconds. |
None
|
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If no parameter is given at all. |
APIError
|
The device does not have one of these parameters
(HTTP 400, e.g. |
Source code in src/aiopikvm/resources/streamer.py
reset(*, timeout=None)
async
¶
Restart the streamer process.
The standard recovery for a pipeline that has frozen or wedged its capture device. Video drops for a moment while ustreamer restarts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float | None
|
Per-call timeout in seconds. |
None
|
Source code in src/aiopikvm/resources/streamer.py
snapshot(*, allow_offline=False, save=False, load=False, preview=False, preview_max_width=None, preview_max_height=None, preview_quality=None, timeout=None)
async
¶
Take a JPEG screenshot.
Without allow_offline, kvmd returns HTTP 503 whenever the video
source is not online (host asleep, HDMI unplugged, etc.). Passing
allow_offline=True makes kvmd return a "NO LIVE VIDEO" placeholder
JPEG instead, and the returned
SnapshotImage.online says which one
arrived. The flag has no effect when the streamer process is fully
stopped (no UI clients) — the call still fails with HTTP 503, unless
load is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_offline
|
bool
|
When |
False
|
save
|
bool
|
Also store this frame as the device's saved snapshot, where
it shows up in
|
False
|
load
|
bool
|
Return the saved snapshot instead of capturing a new one. Works while the streamer is stopped, which is the point. |
False
|
preview
|
bool
|
Have kvmd scale the image down before sending it. The
reported |
False
|
preview_max_width
|
int | None
|
Width bound for the preview. Leaving both bounds unset gives a fifth of the source size; setting only this one leaves the height at the source height. |
None
|
preview_max_height
|
int | None
|
Height bound for the preview. |
None
|
preview_quality
|
int | None
|
JPEG quality of the preview, 1 to 100. |
None
|
timeout
|
float | None
|
Per-call timeout in seconds. |
None
|
Returns:
| Type | Description |
|---|---|
SnapshotImage
|
The JPEG together with the metadata ustreamer reports for it. |
Raises:
| Type | Description |
|---|---|
UnavailableError
|
The video source is offline and
|
Source code in src/aiopikvm/resources/streamer.py
delete_snapshot()
async
¶
get_ocr_info()
async
¶
Get OCR capability metadata (enabled flag, available languages).
Returns:
| Type | Description |
|---|---|
OCRInfo
|
Installed OCR languages and the default selection. |
Source code in src/aiopikvm/resources/streamer.py
ocr(*, langs=None, left=None, top=None, right=None, bottom=None, allow_offline=False, timeout=30.0)
async
¶
Perform OCR on the current screen.
Sends GET /api/streamer/snapshot?ocr=1 — the kvmd snapshot
endpoint with the ocr flag, which returns recognized text as
text/plain instead of a JPEG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
langs
|
list[str] | None
|
Tesseract language codes (e.g. |
None
|
left
|
int | None
|
Left edge of the region to read, in pixels. Cropping is what makes OCR quick: Tesseract needs 10-20 s for a full screen. |
None
|
top
|
int | None
|
Top edge of the region to read. |
None
|
right
|
int | None
|
Right edge of the region to read. |
None
|
bottom
|
int | None
|
Bottom edge of the region to read. |
None
|
allow_offline
|
bool
|
When |
False
|
timeout
|
float
|
Per-call timeout in seconds. OCR runs Tesseract on the Pi CPU and is intrinsically slow (10-20 s for full-screen), so the default is wider than the client-level default. |
30.0
|
Returns:
| Type | Description |
|---|---|
str
|
Recognized text. |