帧注入 API(输出写路径)

App frame injection front end (PushFrame P0-P2).

FramePublisher composes the two halves of the output write path that the daemon exposes: a daemon-side DSP buffer pool (geometry-pinned dma-bufs, NV12 or ARGB32) and the CameraControl PushFrame RPCs (metadata only — the buffer crosses as a DSP-registry id, never as a raw fd number).

class neoruntime_ipc_sdk.injection.FramePublisher(camera, dsp, stream_id='sub', pool_depth=4, *, mode='replace', fmt='nv12', inset=None, dest=(0, 0), session_id='', lease_timeout_s=5.0)[source]

Bases: object

Publish frames into a stream’s encoder feed (REPLACE / OVERLAY).

The publisher resolves the target stream’s geometry from GetStreamStatus at construction and rejects wrong-shaped input client-side, so the daemon never sees a doomed request.

mode="replace" (P0): the pool matches the stream’s encode resolution and the daemon composites each pushed frame over the whole encode output. NV12 only — the RGB escape hatch is publish_rgb().

mode="overlay" (P2): the pool matches inset (default: one quarter of the stream, even-rounded) and each frame is composed at dest inside the live picture. fmt="nv12" insets paste opaquely; fmt="argb" insets alpha-blend on the CPU (wire byte order [A, R, G, B]). Use neoruntime_ipc_sdk.camera.pip_dest() to place a corner-anchored picture-in-picture window.

Frames round-robin across a small daemon-side pool: each publish() writes one slot and pushes that slot’s registry id. On daemons that report the write-lease set (Fix-1) a slot is rewritten only after the daemon acknowledges it is no longer reading it — every PushFrame response carries the ids still queued or mid-bake, and with all slots busy publish() waits for the bake frontier (up to lease_timeout_s) and raises instead of silently tearing pixels. On older daemons the publisher falls back to the legacy blind rotation (a warning is logged once); give such deployments pool_depth >= 4 and publish at stream rate. The daemon-side queue is cap-3 drop-oldest, so publishing never backpressures the encoder; overflow shows up in CameraClient.injection_status() as frames_dropped.

To show the injected stream in a browser, no server is needed: neoruntime_ipc_sdk.web.platform_stream_url() composes the gateway URL of the (now app-affected) platform stream.

Usage:

with FramePublisher(cam, dsp, stream_id="sub") as pub:
    pub.publish(frame)          # uint8 (h*3//2, w) ndarray or tight NV12 bytes
    ...
    pub.publish_eos()           # flush; ISP path restores at the next IDR

x, y = pip_dest(1920, 1080, 480, 270, "bottom-right", 32)
with FramePublisher(cam, dsp, stream_id="main", mode="overlay",
                    fmt="argb", inset=(480, 270), dest=(x, y)) as pub:
    pub.publish(badge_rgba)     # (270, 480, 4) uint8, [A, R, G, B]
__init__(camera, dsp, stream_id='sub', pool_depth=4, *, mode='replace', fmt='nv12', inset=None, dest=(0, 0), session_id='', lease_timeout_s=5.0)[source]
property width: int

Pool width (stream encode width for REPLACE, inset width for OVERLAY).

property height: int

Pool height (stream encode height for REPLACE, inset height for OVERLAY).

property stream_id: str
property session_id: str

Lifecycle tag stamped on every push (”” = untagged, P2-13).

property mode: str
property dest: tuple[int, int]

Paste origin for OVERLAY (always (0, 0) for REPLACE).

property lease_mode: bool

True when the daemon reports the write-lease set (slot-aware publishing); False on legacy daemons (blind rotation).

publish(frame, pts_ns=0)[source]

Write one frame to the next pool slot and push its id.

NV12 pools take a uint8 ndarray shaped (height*3//2, width) or tight NV12 bytes; ARGB32 pools take a uint8 ndarray shaped (height, width, 4) (wire byte order [A, R, G, B]) or tight bytes of width * height * 4. Returns the daemon’s InjectionResult; raises on RPC failure (the pool slot was still written — the daemon-side drop-oldest queue absorbs it, see frames_dropped).

pts_ns paces the bake (device CLOCK_MONOTONIC, the same clock as frame timestamps): 0 = due immediately, a future value holds the frame until the encoder frontier passes it.

Lease mode (daemon reporting in_flight_buffer_ids): the slot is chosen among ids the daemon has released and the response’s lease snapshot replaces the local in-flight set, so a slot is never rewritten while its pixels may still be queued or mid-bake.

property pool: DspBufferPool

The publisher’s daemon-side pool — the slots PushFrame accepts.

Exposed for device-side chains: a DSP job can write this pool directly (dsp.blend_hw(..., out=pub.pool, dst_slot=slot)) and the frame is publishable without a single client-side pixel. The pool dies with the publisher — close() releases it — so hold the publisher open for as long as any writer targets it.

acquire_slot()[source]

Reserve one pool slot for a device-side write.

The lease-aware half of the zero-copy chain: on daemons reporting the write-lease set the returned slot’s buffer id is absent from the queued+mid-bake in-flight set — the same rule publish() applies before writing — so a DSP job may safely target it. Legacy daemons get the blind rotation; the caller owns the pacing there. The reservation is client-side bookkeeping only: the daemon learns the slot is in flight from the subsequent push_slot() response, so push the slot promptly — an acquired-but-never-pushed slot is invisible to the lease set and may be handed out again.

push_slot(slot, pts_ns=0)[source]

Push one already-written pool slot by id (no client write).

The zero-copy publish tail: the slot’s pixels were produced on the DSP straight into pool (blend_hw(..., out=self.pool, dst_slot=slot) — or any *_hw call taking it as dst_pool), so publishing is only the PushFrame RPC. The request is byte-identical to publish()’s; only the write is skipped, and the lease snapshot updates the same way.

publish_stream(frames, pts_ns=None, end_with_eos=False, timeout_s=None)[source]

Push a run of frames over one client-streaming RPC.

frames is an iterable of the same payloads publish() accepts; pts_ns is an optional parallel iterable of due timestamps (missing values default to 0). All requests ride a single PushFrameStream call: the daemon applies per-request validation and the first rejection ends the stream — raised here as RuntimeError with the accepted-frame count in the text. With end_with_eos=True a final EOS request closes the session before the RPC returns; otherwise the stream ends in a clean half-close that leaves the session open (close it later via publish_eos()).

Slots recycle through the same pool as publish(): the generator writes each frame into the next slot as gRPC pulls it. Client-streaming has no per-frame response, so in lease mode the generator blocks on _acquire_slot(), whose freshness comes from injection_status() polling — the single-RPC efficiency is kept, only the write pace adapts. On legacy daemons pass pool_depth >= 4 and push at stream rate: a shallower pool rewrites a slot whose frame may still be queued (the queue then bakes the newer pixels).

publish_rgb(rgb, pts_ns=0)[source]

Convert an RGB frame to NV12 and REPLACE the stream with it.

The daemon’s REPLACE contract is NV12-only, so the RGB path is composed here on the SDK side: one DMA-side rgb24 staging buffer, a hardware CONVERT (DspClient.convert_hw()) into a one-deep NV12 staging pool, then a push of that pool’s buffer id. rgb is a uint8 (height, width, 3) ndarray (RGB order — swap BGR beforehand) or tight bytes. If the DSP refuses or is unavailable the convert falls back to CPU and the result is copied into the staging pool, so the push is always well-defined (dsp.last_used_hw records the path taken).

The staging slot is reused every call: lease mode blocks until the daemon has released the staging NV12 (the pushed id; the rgb staging is only ever read by the convert itself), legacy mode relies on the caller pacing at stream rate — faster publishing lets a still-queued frame bake with the next frame’s pixels.

publish_eos()[source]

Flush the injection session (buffer_id 0 + end_of_stream).

The daemon drops the session and its queue; the pure ISP path restores at the next IDR. Sent automatically — best effort — by the context-manager exit when frames were published and no EOS went out; failure there is a warning, not an error.

close()[source]

Release the buffer pools (idempotent; sends no EOS itself).

The context-manager exit sends a best-effort EOS first when frames were published without one; a bare close() never does — callers needing explicit control call publish_eos().

FramePublisher

class neoruntime_ipc_sdk.FramePublisher(camera, dsp, stream_id='sub', pool_depth=4, *, mode='replace', fmt='nv12', inset=None, dest=(0, 0), session_id='', lease_timeout_s=5.0)[source]

Bases: object

Publish frames into a stream’s encoder feed (REPLACE / OVERLAY).

The publisher resolves the target stream’s geometry from GetStreamStatus at construction and rejects wrong-shaped input client-side, so the daemon never sees a doomed request.

mode="replace" (P0): the pool matches the stream’s encode resolution and the daemon composites each pushed frame over the whole encode output. NV12 only — the RGB escape hatch is publish_rgb().

mode="overlay" (P2): the pool matches inset (default: one quarter of the stream, even-rounded) and each frame is composed at dest inside the live picture. fmt="nv12" insets paste opaquely; fmt="argb" insets alpha-blend on the CPU (wire byte order [A, R, G, B]). Use neoruntime_ipc_sdk.camera.pip_dest() to place a corner-anchored picture-in-picture window.

Frames round-robin across a small daemon-side pool: each publish() writes one slot and pushes that slot’s registry id. On daemons that report the write-lease set (Fix-1) a slot is rewritten only after the daemon acknowledges it is no longer reading it — every PushFrame response carries the ids still queued or mid-bake, and with all slots busy publish() waits for the bake frontier (up to lease_timeout_s) and raises instead of silently tearing pixels. On older daemons the publisher falls back to the legacy blind rotation (a warning is logged once); give such deployments pool_depth >= 4 and publish at stream rate. The daemon-side queue is cap-3 drop-oldest, so publishing never backpressures the encoder; overflow shows up in CameraClient.injection_status() as frames_dropped.

To show the injected stream in a browser, no server is needed: neoruntime_ipc_sdk.web.platform_stream_url() composes the gateway URL of the (now app-affected) platform stream.

Usage:

with FramePublisher(cam, dsp, stream_id="sub") as pub:
    pub.publish(frame)          # uint8 (h*3//2, w) ndarray or tight NV12 bytes
    ...
    pub.publish_eos()           # flush; ISP path restores at the next IDR

x, y = pip_dest(1920, 1080, 480, 270, "bottom-right", 32)
with FramePublisher(cam, dsp, stream_id="main", mode="overlay",
                    fmt="argb", inset=(480, 270), dest=(x, y)) as pub:
    pub.publish(badge_rgba)     # (270, 480, 4) uint8, [A, R, G, B]
__init__(camera, dsp, stream_id='sub', pool_depth=4, *, mode='replace', fmt='nv12', inset=None, dest=(0, 0), session_id='', lease_timeout_s=5.0)[source]
property width: int

Pool width (stream encode width for REPLACE, inset width for OVERLAY).

property height: int

Pool height (stream encode height for REPLACE, inset height for OVERLAY).

property stream_id: str
property session_id: str

Lifecycle tag stamped on every push (”” = untagged, P2-13).

property mode: str
property dest: tuple[int, int]

Paste origin for OVERLAY (always (0, 0) for REPLACE).

property lease_mode: bool

True when the daemon reports the write-lease set (slot-aware publishing); False on legacy daemons (blind rotation).

publish(frame, pts_ns=0)[source]

Write one frame to the next pool slot and push its id.

NV12 pools take a uint8 ndarray shaped (height*3//2, width) or tight NV12 bytes; ARGB32 pools take a uint8 ndarray shaped (height, width, 4) (wire byte order [A, R, G, B]) or tight bytes of width * height * 4. Returns the daemon’s InjectionResult; raises on RPC failure (the pool slot was still written — the daemon-side drop-oldest queue absorbs it, see frames_dropped).

pts_ns paces the bake (device CLOCK_MONOTONIC, the same clock as frame timestamps): 0 = due immediately, a future value holds the frame until the encoder frontier passes it.

Lease mode (daemon reporting in_flight_buffer_ids): the slot is chosen among ids the daemon has released and the response’s lease snapshot replaces the local in-flight set, so a slot is never rewritten while its pixels may still be queued or mid-bake.

property pool: DspBufferPool

The publisher’s daemon-side pool — the slots PushFrame accepts.

Exposed for device-side chains: a DSP job can write this pool directly (dsp.blend_hw(..., out=pub.pool, dst_slot=slot)) and the frame is publishable without a single client-side pixel. The pool dies with the publisher — close() releases it — so hold the publisher open for as long as any writer targets it.

acquire_slot()[source]

Reserve one pool slot for a device-side write.

The lease-aware half of the zero-copy chain: on daemons reporting the write-lease set the returned slot’s buffer id is absent from the queued+mid-bake in-flight set — the same rule publish() applies before writing — so a DSP job may safely target it. Legacy daemons get the blind rotation; the caller owns the pacing there. The reservation is client-side bookkeeping only: the daemon learns the slot is in flight from the subsequent push_slot() response, so push the slot promptly — an acquired-but-never-pushed slot is invisible to the lease set and may be handed out again.

push_slot(slot, pts_ns=0)[source]

Push one already-written pool slot by id (no client write).

The zero-copy publish tail: the slot’s pixels were produced on the DSP straight into pool (blend_hw(..., out=self.pool, dst_slot=slot) — or any *_hw call taking it as dst_pool), so publishing is only the PushFrame RPC. The request is byte-identical to publish()’s; only the write is skipped, and the lease snapshot updates the same way.

publish_stream(frames, pts_ns=None, end_with_eos=False, timeout_s=None)[source]

Push a run of frames over one client-streaming RPC.

frames is an iterable of the same payloads publish() accepts; pts_ns is an optional parallel iterable of due timestamps (missing values default to 0). All requests ride a single PushFrameStream call: the daemon applies per-request validation and the first rejection ends the stream — raised here as RuntimeError with the accepted-frame count in the text. With end_with_eos=True a final EOS request closes the session before the RPC returns; otherwise the stream ends in a clean half-close that leaves the session open (close it later via publish_eos()).

Slots recycle through the same pool as publish(): the generator writes each frame into the next slot as gRPC pulls it. Client-streaming has no per-frame response, so in lease mode the generator blocks on _acquire_slot(), whose freshness comes from injection_status() polling — the single-RPC efficiency is kept, only the write pace adapts. On legacy daemons pass pool_depth >= 4 and push at stream rate: a shallower pool rewrites a slot whose frame may still be queued (the queue then bakes the newer pixels).

publish_rgb(rgb, pts_ns=0)[source]

Convert an RGB frame to NV12 and REPLACE the stream with it.

The daemon’s REPLACE contract is NV12-only, so the RGB path is composed here on the SDK side: one DMA-side rgb24 staging buffer, a hardware CONVERT (DspClient.convert_hw()) into a one-deep NV12 staging pool, then a push of that pool’s buffer id. rgb is a uint8 (height, width, 3) ndarray (RGB order — swap BGR beforehand) or tight bytes. If the DSP refuses or is unavailable the convert falls back to CPU and the result is copied into the staging pool, so the push is always well-defined (dsp.last_used_hw records the path taken).

The staging slot is reused every call: lease mode blocks until the daemon has released the staging NV12 (the pushed id; the rgb staging is only ever read by the convert itself), legacy mode relies on the caller pacing at stream rate — faster publishing lets a still-queued frame bake with the next frame’s pixels.

publish_eos()[source]

Flush the injection session (buffer_id 0 + end_of_stream).

The daemon drops the session and its queue; the pure ISP path restores at the next IDR. Sent automatically — best effort — by the context-manager exit when frames were published and no EOS went out; failure there is a warning, not an error.

close()[source]

Release the buffer pools (idempotent; sends no EOS itself).

The context-manager exit sends a best-effort EOS first when frames were published without one; a bare close() never does — callers needing explicit control call publish_eos().

InjectionResult

class neoruntime_ipc_sdk.InjectionResult(success, message, error_code=0, injected_frame_id=0, accepted_frame_count=0, session_id='', in_flight_buffer_ids=())[source]

Result of one CameraClient.push_frame call (PushFrameResponse).

success: bool
message: str
error_code: int = 0
injected_frame_id: int = 0
accepted_frame_count: int = 0
session_id: str = ''
in_flight_buffer_ids: tuple[int, ...] = ()
__init__(success, message, error_code=0, injected_frame_id=0, accepted_frame_count=0, session_id='', in_flight_buffer_ids=())

InjectionStatus

class neoruntime_ipc_sdk.InjectionStatus(success, message, active=False, mode='replace', frames_injected=0, frames_dropped=0, queue_depth=0, session_id='', in_flight_buffer_ids=(), reports_in_flight_buffers=False)[source]

Snapshot of the daemon-side injection session (GetInjectionStatus).

success: bool
message: str
active: bool = False
mode: str = 'replace'
frames_injected: int = 0
frames_dropped: int = 0
queue_depth: int = 0
session_id: str = ''
in_flight_buffer_ids: tuple[int, ...] = ()
reports_in_flight_buffers: bool = False
__init__(success, message, active=False, mode='replace', frames_injected=0, frames_dropped=0, queue_depth=0, session_id='', in_flight_buffer_ids=(), reports_in_flight_buffers=False)

CameraClient 注入 RPC

class neoruntime_ipc_sdk.CameraClient(endpoint=None)[source]

Camera pipeline control client.

Usage:

cam = CameraClient()

# ISP
cam.set_isp(brightness=60, contrast=50)
isp = cam.get_isp()

# Encoder
cam.set_encoder("main", bitrate_bps=8_000_000)

# RTSP
cam.set_rtsp_enabled(True)

# Streams
streams = cam.get_stream_status()
cam.add_stream("third", 1920, 1080, 30, "h264", 4_000_000, 30)

# Profiles
cam.switch_profile("night")
cam.backup_profile()

# Capabilities
caps = cam.get_capabilities()
set_isp(config=None, **kwargs)[source]

Update ISP image pipeline settings.

get_isp()[source]

Get current ISP configuration.

get_transform()[source]
set_transform(cfg)[source]
set_encoder(stream_name='main', bitrate_bps=0, framerate=0, gop=0)[source]

Dynamic encoder config (no restart).

reconfigure_encoder(stream_name, width=0, height=0, codec='', bitrate_bps=0, fps=0, gop=0)[source]

Full encoder reconfiguration (brief restart, ~100ms).

set_rtsp_enabled(enabled)[source]
set_osd(streams)[source]

Update OSD text and datetime overlays per stream.

Parameters:

streams (list[dict]) – list of dicts with keys: stream_name, text_overlays (list of dicts), datetime_overlays (list of dicts)

set_ai_overlay(enabled, show_label=True, show_confidence=True, line_thickness=2)[source]
set_imaging_mode(mode)[source]

Switch day/night imaging mode.

Parameters:

mode (str) – “day”, “infrared” or “auto” (light-sensor driven).

Returns: the resulting InfraredStatus.

get_infrared_status()[source]

Current day/night imaging state (mode, PWMs, light sensor).

set_infrared_settings(auto_follow=None, near_pwm=None, far_pwm=None, night_enter=None, day_enter=None)[source]

Update IR light settings; omitted fields (None) are left unchanged.

Parameters:
  • auto_follow (bool | None) – tie IR output to optical zoom ratio.

  • far_pwm (near_pwm /) – manual IR intensities (0-100).

  • day_enter (night_enter /) – light-sensor thresholds for auto mode.

clear_infrared_manual()[source]

Drop manual IR overrides and return to profile-driven output.

list_ir_presets()[source]

Saved IR-light profiles (per zoom ratio).

save_ir_preset(name, zoom_ratio, near_pwm, far_pwm)[source]

Save (or overwrite) an IR preset and return the new list.

delete_ir_preset(name)[source]

Delete an IR preset by name and return the remaining list.

get_privacy_mask()[source]

Current static + dynamic (AI) privacy-mask configuration.

set_privacy_mask(settings=None, *, color=None, blur_radius=None, enabled=None, regions=None, dpm_enabled=None, dpm_labels=None, dpm_mode=None, dpm_color=None)[source]

Update privacy-mask configuration with merge semantics.

Only the fields given as arguments are changed; everything else (including existing regions) is read back from the device and preserved, so partial updates never wipe masks.

Parameters:
  • settings (PrivacyMaskSettings | None) – full PrivacyMaskSettings to apply wholesale (optional).

  • color (int | None) – 0x00RRGGBB fill color (used when blur_radius=0).

  • blur_radius (int | None) – pixelation block size 2-64; 0 = solid color.

  • enabled (bool | None) – global static-mask on/off.

  • regions (list[dict] | None) – list of {id, name, enabled, points_x, points_y} with normalized polygon coordinates (up to 8 points).

  • dpm_* – dynamic (AI) privacy-mask options.

get_osd()[source]

Read back OSD overlay config, shaped like set_osd() input.

Returns: list of {stream_name, text_overlays, datetime_overlays, image_overlays}; each overlay is a plain dict of its proto fields, so the result can be edited and fed straight back into set_osd().

get_config_field(field_path)[source]

Read one camera-daemon config field by dotted path.

Parameters:

field_path (str) – e.g. “frontend.hailort.use-hailort-service”.

Returns: the current value encoded as a string.

set_config_field(field_path, value)[source]

Write one camera-daemon config field by dotted path.

Parameters:
  • field_path (str) – dotted config path.

  • value – value encoded as a string (e.g. “true”, “42”, “1.5”); non-str values are converted with str().

get_stream_status(timeout_s=None)[source]

List live streams with encoder state; timeout_s bounds the RPC (None waits indefinitely, gRPC’s default).

push_frame(buffer_id, width, height, stride, mode='replace', pts_ns=0, dest_x=0, dest_y=0, stream_id='', end_of_stream=False, session_id='', timeout_s=None)[source]

Push one frame into the daemon’s encoder feed (PushFrame).

buffer_id must be a DSP-registry buffer id (see DspBufferPool.buffer_id / DspClient.import_frame()) — a raw fd number never crosses gRPC. The registered buffer’s geometry must equal width/height.

Modes (P2):

  • "replace" — full-frame NV12 content copy over one stream’s encode output. stream_id empty keeps the P0 semantics (any stream whose encode dims match); an explicit stream_id targets that stream and requires dims equal to its encode resolution.

  • "overlay" — compose on top of the live stream. Requires stream_id. dest_x/dest_y (even, NV12 chroma alignment) position the inset. NV12 insets paste opaquely; ARGB32 insets alpha-blend on the CPU.

pts_ns paces the bake (device CLOCK_MONOTONIC domain, same clock as frame timestamps): 0 = due immediately; a future value holds the frame until the encoder frontier passes it, and a newer due frame supersedes (drops) older due frames for the same target. end_of_stream=True (buffer_id 0) flushes the session and restores the ISP path at the next IDR.

session_id tags the injection session (P2-13 lifecycle): correlation and observability only — the daemon anchors the session on the buffer owner’s UDS connection and closes it on that client’s disconnect regardless of the tag.

push_frame_stream(frames, timeout_s=None)[source]

Push a run of frames over one client-streaming RPC (PushFrameStream).

frames is an iterable (or generator) of keyword dicts, each accepted by push_frame() — e.g. {"buffer_id": bid, "width": w, "height": h, "stride": s, "mode": "overlay", "stream_id": "main", "dest_x": 64, "dest_y": 64}. A dict with end_of_stream=True closes the injection session server-side before the RPC returns; omitting any EOS dict ends the stream with a clean half-close that LEAVES the session open (close later via stop_injection()).

Per-request semantics are identical to unary push_frame(); the first server-side rejection ends the stream and is re-raised here as RuntimeError (the accepted-frame count at that point rides the exception text). A session_id key in any request dict tags the session (P2-13) — the daemon records the latest non-empty tag and echoes it on the response.

Returns an InjectionResult whose accepted_frame_count reports how many frames the daemon accepted before the stream ended.

injection_status(timeout_s=None)[source]

Injection session snapshot: active, mode, counters, queue depth, the live session’s lifecycle tag (empty when untagged/closed), and the write-lease set (in_flight_buffer_ids + reports_in_flight_buffers; Fix-1).

stop_injection(timeout_s=None)[source]

Close the injection session and restore the ISP path (next IDR).

add_stream(stream_id, width, height, fps, codec='h264', bitrate=4000000, gop=30)[source]
remove_stream(stream_name)[source]
reconfigure_pipeline(streams)[source]
get_profile()[source]
list_profiles()[source]

Returns (profile_names, current_profile).

switch_profile(name)[source]
backup_profile(path='')[source]
get_sensor_info(sensor_index=0)[source]
get_capabilities()[source]
get_hardware_status()[source]
set_led_duty(led_id, duty_percent)[source]
get_led_duty(led_id)[source]
set_ircut(mode)[source]

Set IR-cut filter. mode: 0=day, 1=night. Returns current mode.

get_ircut()[source]
mcu_raw_request(cmd, payload=b'')[source]
set_fan(enable)[source]
get_fan()[source]
set_heat(enable)[source]
get_heat()[source]
set_radar(enable)[source]
get_radar()[source]
set_alarm_out(channel, enable)[source]
get_alarm_out(channel)[source]
get_alarm_outputs()[source]
rs485_init(baudrate=9600, config='8N1')[source]
rs485_deinit()[source]
rs485_tx(data)[source]

使用示例

把处理后的 NV12 帧发布到子码流编码器

from neoruntime_ipc_sdk import CameraClient, DspClient, FramePublisher

cam = CameraClient()
dsp = DspClient()

# 构造时从 GetStreamStatus 解析子码流编码分辨率并锁定几何;
# 之后 publish 只接受该分辨率的紧凑 NV12(ndarray 或 bytes)
with FramePublisher(cam, dsp, stream_id="sub") as pub:
    for frame in processed_frames:      # uint8 (h*3//2, w)
        pub.publish(frame)

    pub.publish_eos()   # 结束注入会话;下个 IDR 起恢复纯 ISP 画面

语义要点

  • 两种模式:mode="replace"``(P0)注入帧整体替换编码输入; ``mode="overlay"``(P2)把小画面合入实况画面(``fmt="nv12" 为不 透明贴块,fmt="argb" 为 CPU alpha 混合,几何由 inset/dest 决定)。

  • 帧跨进程以 DSP 注册 id 传递,不是裸 fd —— 与取帧(FrameHandle)、 零拷贝推理(infer(frame))共用同一套缓冲命名空间。

  • daemon 侧队列 cap 3、满则丢最旧:publish 永不反压编码器, 溢出体现在 injection_status().frames_dropped。

  • 写租约(池槽不被提前复用):daemon 在 PushFrame 响应与 注入状态里回报”仍在读”的缓冲 id(排队中 + 合成中);publish 家族 只写确认空闲的池槽,池满时等待释放,超过 lease_timeout_s (默认 5s)抛明确错误——不再静默覆写仍在用的像素。默认 pool_depth=4。对不回报该集合的旧版 daemon 自动退回旧的 深度轮转并告警一次(lease_mode 只读属性可查当前模式)。

  • 结束即恢复:publish_eos()``(或 ``stop_injection())后, 纯 ISP 画面从下个 IDR 起恢复,无需重启码流。

  • 生命周期标记(P2-13):session_id= 给注入会话打标记—— 首个带标记的帧打开会话,标记可在 injection_status().session_id 观测。回收以连接为键、不以标记为键:注入方断连(含进程被 SIGKILL) 时 daemon 立即关闭会话并释放该客户端的全部缓冲,无论标记为何。

  • close() 只释放缓冲池、不发 EOS —— 结束会话必须显式调 publish_eos()。