feat: GET /api/color/latest, the fast color channel (no align)

JPEG of the latest color frame at the camera rate, not aligned to depth.
after=N waits up to timeout_ms (default 1000, max 5000) for a frame numbered
above N, so a client reads every frame without polling on empty; 504 when
none arrives. Headers: X-Frame-Seq, X-Camera-Timestamp-Ms (camera clock),
X-Camera-Timestamp-Domain, X-Host-Timestamp (arrival on the host).
Each color frame is encoded once for all clients (EncodedColor, own cache of
4: the color rate would flush the aligned frames' cache). New timing phase
jpeg_color. /api/rgb is unchanged: still the color of the aligned frame, the
same frame as /api/depth (README says so next to the new row).
color_frame() without after waits for the very first frame after open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
OliviaCandClaude Opus 5.5 committed 2026-09-30 16:53:44 +02:00
1 parent 47ebc4fbc0
commit 6a442e9f32
3 files changed
+81 -7

No files matched your search

+5 -5
View File
@@ -454,15 +454,15 @@ class RealSenseD435:
def color_frame(self, after_seq: Optional[int] = None, timeout_ms: int = 1000) -> tuple[int, Optional[ColorFrame]]:
"""(number, image) of the fast color channel: the latest color frame, no align.
With `after_seq`, waits up to `timeout_ms` for a frame numbered above it.
With `after_seq`, waits up to `timeout_ms` for a frame numbered above it;
without, returns the latest one (waiting only for the very first).
"""
raw = self._raw
if not self._open or raw is None:
return 0, None
if after_seq is None:
seq, published = raw.latest()
else:
seq, published = raw.wait_newer(after_seq, timeout_ms / 1000.0)
seq, published = raw.latest()
if after_seq is not None or published is None:
seq, published = raw.wait_newer(after_seq or 0, timeout_ms / 1000.0)
return seq, (published.color if published is not None else None)
def _grab_raw(self, timeout_ms: int) -> Optional[RawFrameset]: