Recording API
Recording utilities - pure-python MPEG-TS muxing for EncodedFrame streams.
TsWriter writes a single .ts event clip; HlsWriter cuts keyframe-aligned segments plus an m3u8 live playlist; PrerollBuffer keeps a ring of encoded frames so an app can dump “seconds before the event” when something happens.
No ffmpeg is required: Annex-B payloads from the EncodedPublisher are packetised into PES/TS directly, without re-encoding.
with HlsWriter("/var/tmp/hls", segment_seconds=6.0) as hls:
for frame in client.subscribe_encoded("main"):
hls.write(frame)
# serve /var/tmp/hls/index.m3u8 over HTTP for hls.js
- class neoruntime_ipc_sdk.recording.TsWriter(path, codec='h264')[source]
Bases:
objectSingle-file MPEG-TS recorder for EncodedFrame Annex-B payloads.
w = TsWriter("event.ts", codec="h264") for frame in client.subscribe_encoded("main"): w.write(frame) w.close()
- __init__(path, codec='h264')[source]
- write(frame)[source]
Packetise one encoded frame into the TS stream.
- close()[source]
- class neoruntime_ipc_sdk.recording.HlsWriter(out_dir, segment_seconds=6.0, window=5, codec='h264')[source]
Bases:
objectKeyframe-aligned HLS segmenter with a live-window m3u8 playlist.
Every segment is self-contained (PAT+PMT first) so hls.js can join the stream at any segment boundary. The playlist is rewritten atomically (tmp + os.replace) after every segment close.
hls = HlsWriter("/srv/hls", segment_seconds=6.0, window=5) for frame in client.subscribe_encoded("main"): hls.write(frame) # blocks of frames -> seg000001.ts ... hls.close() # appends #EXT-X-ENDLIST
- __init__(out_dir, segment_seconds=6.0, window=5, codec='h264')[source]
- write(frame)[source]
Append a frame; cuts a segment on keyframes once it is long enough.
- close()[source]
Finalise the last segment and end the playlist.
- class neoruntime_ipc_sdk.recording.PrerollBuffer(seconds=10.0)[source]
Bases:
objectRing buffer of encoded frames for “seconds before the event” clips.
preroll = PrerollBuffer(seconds=10.0) for frame in client.subscribe_encoded("main"): preroll.push(frame) if event_detected: writer = preroll.dump("event.ts") # keep writing live frames to `writer`, then writer.close()
- __init__(seconds=10.0)[source]
- push(frame)[source]
Add a frame and evict frames older than the window.
- property frames: list[EncodedFrame]
- dump(path, on_frame=None)[source]
Flush buffered preroll into a new .ts file and return the writer.
on_frame(writer) is called right after the preroll flush - the typical use is starting a live-tail thread that keeps calling writer.write() until the event window ends.
TsWriter
HlsWriter
- class neoruntime_ipc_sdk.HlsWriter(out_dir, segment_seconds=6.0, window=5, codec='h264')[source]
Keyframe-aligned HLS segmenter with a live-window m3u8 playlist.
Every segment is self-contained (PAT+PMT first) so hls.js can join the stream at any segment boundary. The playlist is rewritten atomically (tmp + os.replace) after every segment close.
hls = HlsWriter("/srv/hls", segment_seconds=6.0, window=5) for frame in client.subscribe_encoded("main"): hls.write(frame) # blocks of frames -> seg000001.ts ... hls.close() # appends #EXT-X-ENDLIST
PrerollBuffer
- class neoruntime_ipc_sdk.PrerollBuffer(seconds=10.0)[source]
Ring buffer of encoded frames for “seconds before the event” clips.
preroll = PrerollBuffer(seconds=10.0) for frame in client.subscribe_encoded("main"): preroll.push(frame) if event_detected: writer = preroll.dump("event.ts") # keep writing live frames to `writer`, then writer.close()
- property frames: list[EncodedFrame]
Usage Examples
Record MP4 (TsWriter)
from neoruntime_ipc_sdk import EncodedMediaClient, TsWriter
media = EncodedMediaClient()
writer = TsWriter("/data/aipc/recordings/clip.mp4", codec="h264")
try:
# Subscribe to the encoded stream and write frames by PTS
for frame in media.subscribe("main"):
writer.write(frame)
if enough_recorded(frame):
break
finally:
writer.close() # must close to finalize the moov/index
HLS live segmentation
from neoruntime_ipc_sdk import EncodedMediaClient, HlsWriter
media = EncodedMediaClient()
# One 6s segment, playlist keeps the latest 5 segments
hls = HlsWriter("/data/aipc/recordings/live", segment_seconds=6.0, window=5)
try:
for frame in media.subscribe("main"):
hls.write(frame)
finally:
hls.close()
Preroll recording (pre-event cache)
from neoruntime_ipc_sdk import EncodedMediaClient, PrerollBuffer
media = EncodedMediaClient()
# Ring buffer holding the last 10s of encoded frames
preroll = PrerollBuffer(seconds=10.0)
for frame in media.subscribe("main"):
preroll.push(frame)
if event_detected():
# dump: preroll contents + subsequent frames -> ts file
writer = preroll.dump("/data/aipc/recordings/alert.ts")
# ... keep appending post-event frames via writer.write(frame)
writer.close()
break
Custom frame callback
# dump's on_frame runs before each frame is written — use it to
# filter or modify frames
def mark(frame):
print(f"writing pts={frame.pts_ns}")
return frame
writer = preroll.dump("/data/aipc/recordings/alert.ts", on_frame=mark)