reactor-runtime 2.7.6__tar.gz → 2.7.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/PKG-INFO +1 -1
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/pyproject.toml +1 -1
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/internal/output_buffer.py +46 -87
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/session_recorder.py +14 -2
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtime_api.py +10 -13
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/headless/headless_runtime.py +35 -17
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/PKG-INFO +1 -1
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/README.md +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/setup.cfg +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/api/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/experiment/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/experiment/session.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/defaults.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/driver/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/driver/pipeline_executor.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/driver/step_result.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/connected.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/event.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/messages.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/upload.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/internal/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/internal/input_buffer.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/internal/reactor_core.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/decorators.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/handlers.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/reactor_model.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/idle.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/input_state.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/reactor_pipeline.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/descriptors.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/input.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/output.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/upload.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/model_state.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/base.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/file.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/otlp.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/helpers.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/nvml_sampler.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/plotting/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/plotting/plot_profiling.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/profiler.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/singleton.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/torch_chunk_profiler.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/chunk_encoder.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/chunk_uploader.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/markers.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/sinks.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/track_resolver.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/headless/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/headless/input_feeder.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/http/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/http/http_runtime.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/http/types.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/schema.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/schema_validator.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/__main__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/commands/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/commands/run.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/commands/schema.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/main.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/utils/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/utils/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/utils/runtime.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/audio_track.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/client.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/frame_conversion.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/ice_connection.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/video_track.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/config.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/events.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/client.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/av1.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/base.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/factory.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/h264.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/h265.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/opus.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/vp8.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/decoders/vp9.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/av1.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/base.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/factory.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/h264.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/h265.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/opus.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/vp8.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/encoders/vp9.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/gst.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/gst_helpers.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/probes/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/probes/fps_probe.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/receiver/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/receiver/audio.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/receiver/base.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/receiver/video.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/bundle.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/codec.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/extmap.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/ice.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sender/__init__.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sender/audio.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sender/base.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sender/video.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/settings.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/signals.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/ice_uris.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/interface.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/media.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/types.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/launch.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/loader.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/log.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/messages.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/paths.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/ports.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/utils/typing.py +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/SOURCES.txt +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/dependency_links.txt +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/requires.txt +0 -0
- {reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/top_level.txt +0 -0
|
@@ -105,25 +105,9 @@ def split_batch(bundle: MediaBundle) -> List[MediaBundle]:
|
|
|
105
105
|
class _FlushMarker:
|
|
106
106
|
"""Sentinel placed in :class:`OutputBuffer`'s queue by :meth:`flush`.
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
``_create_black_bundle()`` with ``duplicate=True``).
|
|
112
|
-
|
|
113
|
-
The point of routing the session-boundary reset through the queue
|
|
114
|
-
rather than writing ``_last_emitted = None`` directly from
|
|
115
|
-
:meth:`flush` is to make the reset atomic with respect to the
|
|
116
|
-
emission thread: only the emission thread reads or writes
|
|
117
|
-
``_last_emitted``, so the loop can never observe a half-applied
|
|
118
|
-
"queue drained but cached frame not yet cleared" state. Down-stream
|
|
119
|
-
callbacks already drop ``duplicate=True`` bundles (the wire
|
|
120
|
-
callback in ``_send_out_app_bundle_sync`` short-circuits, and the
|
|
121
|
-
:class:`~reactor_runtime.recording.session_recorder.SessionRecorder`
|
|
122
|
-
skips them when ``skip_leading_black`` is on and
|
|
123
|
-
``recording_started`` is still False), so no stale frame would
|
|
124
|
-
reach the client or recording even without this guarantee — but
|
|
125
|
-
closing the race architecturally is cheaper than re-deriving that
|
|
126
|
-
safety argument for every callback added in the future.
|
|
108
|
+
Consumed by the emission thread, which resets ``_last_emitted`` and
|
|
109
|
+
dispatches a fresh black bundle with ``is_fresh_black=True``. Doing
|
|
110
|
+
the reset in-thread keeps it atomic with the per-tick state read.
|
|
127
111
|
"""
|
|
128
112
|
|
|
129
113
|
|
|
@@ -152,13 +136,11 @@ class OutputBuffer:
|
|
|
152
136
|
# Ordered list of per-tick observers. Callbacks fire in
|
|
153
137
|
# registration order on every emission tick; an exception in
|
|
154
138
|
# one callback is logged and does not block the others.
|
|
155
|
-
self._callbacks: List[Callable[[MediaBundle, bool], None]] = []
|
|
139
|
+
self._callbacks: List[Callable[[MediaBundle, bool, bool], None]] = []
|
|
156
140
|
self._callbacks_lock: threading.Lock = threading.Lock()
|
|
157
141
|
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
# both shapes are valid; the emission loop discriminates with
|
|
161
|
-
# ``isinstance``.
|
|
142
|
+
# Holds MediaBundle plus the _FlushMarker sentinel; the
|
|
143
|
+
# emission loop discriminates by isinstance.
|
|
162
144
|
self._q: queue.Queue[object] = queue.Queue(maxsize=queue_depth)
|
|
163
145
|
|
|
164
146
|
# FPS control — store both rate and period to avoid 1/fps on every tick
|
|
@@ -182,29 +164,18 @@ class OutputBuffer:
|
|
|
182
164
|
# Callbacks
|
|
183
165
|
# ------------------------------------------------------------------
|
|
184
166
|
|
|
185
|
-
def add_callback(self, fn: Callable[[MediaBundle, bool], None]) -> None:
|
|
186
|
-
"""Register a per-tick callback
|
|
167
|
+
def add_callback(self, fn: Callable[[MediaBundle, bool, bool], None]) -> None:
|
|
168
|
+
"""Register a per-tick callback ``(bundle, duplicate, is_fresh_black)``.
|
|
187
169
|
|
|
188
|
-
Idempotent
|
|
189
|
-
|
|
190
|
-
:meth:`remove_callback` which has always been a no-op on
|
|
191
|
-
already-unregistered functions — callers shouldn't have to
|
|
192
|
-
track registration state.
|
|
193
|
-
|
|
194
|
-
The callback receives ``(bundle, duplicate)`` on every emission
|
|
195
|
-
tick. Callbacks fire in registration order and are isolated
|
|
196
|
-
from each other: an exception raised by one is logged and the
|
|
197
|
-
remaining callbacks still fire.
|
|
170
|
+
Idempotent — re-registering the same ``fn`` is a no-op.
|
|
171
|
+
Callbacks fire in registration order with exceptions isolated.
|
|
198
172
|
"""
|
|
199
173
|
with self._callbacks_lock:
|
|
200
174
|
if fn not in self._callbacks:
|
|
201
175
|
self._callbacks.append(fn)
|
|
202
176
|
|
|
203
|
-
def remove_callback(self, fn: Callable[[MediaBundle, bool], None]) -> None:
|
|
204
|
-
"""Deregister a previously added callback.
|
|
205
|
-
|
|
206
|
-
No-op if *fn* was never registered.
|
|
207
|
-
"""
|
|
177
|
+
def remove_callback(self, fn: Callable[[MediaBundle, bool, bool], None]) -> None:
|
|
178
|
+
"""Deregister a previously added callback (no-op if not registered)."""
|
|
208
179
|
with self._callbacks_lock:
|
|
209
180
|
try:
|
|
210
181
|
self._callbacks.remove(fn)
|
|
@@ -320,27 +291,30 @@ class OutputBuffer:
|
|
|
320
291
|
# Stage 3: Emission loop
|
|
321
292
|
# ------------------------------------------------------------------
|
|
322
293
|
|
|
323
|
-
def _dispatch(
|
|
294
|
+
def _dispatch(
|
|
295
|
+
self,
|
|
296
|
+
bundle: MediaBundle,
|
|
297
|
+
duplicate: bool,
|
|
298
|
+
*,
|
|
299
|
+
is_fresh_black: bool = False,
|
|
300
|
+
) -> None:
|
|
324
301
|
"""Fire every registered callback in order with error isolation.
|
|
325
302
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
303
|
+
Callbacks receive ``(bundle, duplicate, is_fresh_black)``.
|
|
304
|
+
``is_fresh_black`` is ``True`` only on the synthesised black
|
|
305
|
+
the sentinel path emits at session boundaries; callbacks
|
|
306
|
+
choose how to interpret that separately from ``duplicate``.
|
|
307
|
+
Exceptions in one callback are logged and don't block the
|
|
308
|
+
rest.
|
|
330
309
|
"""
|
|
331
310
|
with self._callbacks_lock:
|
|
332
311
|
callbacks = list(self._callbacks)
|
|
333
312
|
for fn in callbacks:
|
|
334
313
|
try:
|
|
335
|
-
fn(bundle, duplicate)
|
|
314
|
+
fn(bundle, duplicate, is_fresh_black)
|
|
336
315
|
except Exception as ex:
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
# structured fields so log queries on ``exc_type`` /
|
|
340
|
-
# ``exc_msg`` / ``callback`` work without traceback
|
|
341
|
-
# parsing. Without these explicit fields a silent
|
|
342
|
-
# malfunction in a downstream callback looks like
|
|
343
|
-
# "feature X didn't fire" with no obvious cause.
|
|
316
|
+
# Structured fields so log queries on exc_type /
|
|
317
|
+
# exc_msg / callback work without traceback parsing.
|
|
344
318
|
logger.exception(
|
|
345
319
|
"OutputBuffer callback raised; continuing with remaining callbacks",
|
|
346
320
|
callback=repr(fn),
|
|
@@ -361,19 +335,24 @@ class OutputBuffer:
|
|
|
361
335
|
item = None
|
|
362
336
|
|
|
363
337
|
bundle: Optional[MediaBundle] = None
|
|
338
|
+
sentinel_consumed = False
|
|
364
339
|
if isinstance(item, _FlushMarker):
|
|
365
|
-
# Session-boundary reset, processed in the emission
|
|
366
|
-
# thread so there is no cross-thread race with
|
|
367
|
-
# flush(): clear the cached frame and fall through
|
|
368
|
-
# to the empty-queue fallback below, which
|
|
369
|
-
# dispatches duplicate=True black. mark_first_real_frame()
|
|
370
|
-
# is gated on duplicate=False, so the recorder's
|
|
371
|
-
# latch (REA-2323 / #2325) stays unset.
|
|
372
340
|
self._last_emitted = None
|
|
341
|
+
sentinel_consumed = True
|
|
373
342
|
elif isinstance(item, MediaBundle):
|
|
374
343
|
bundle = item
|
|
375
344
|
|
|
376
|
-
if
|
|
345
|
+
if sentinel_consumed:
|
|
346
|
+
# Session-boundary black: tagged so the wire
|
|
347
|
+
# forwards it (otherwise the client would stay
|
|
348
|
+
# frozen on the previous frame), the recorder
|
|
349
|
+
# ignores it.
|
|
350
|
+
self._dispatch(
|
|
351
|
+
self._create_black_bundle(),
|
|
352
|
+
True,
|
|
353
|
+
is_fresh_black=True,
|
|
354
|
+
)
|
|
355
|
+
elif bundle is not None:
|
|
377
356
|
vtracks = bundle.get_tracks_by_kind(TrackKind.VIDEO)
|
|
378
357
|
if vtracks:
|
|
379
358
|
vd = vtracks[0].data
|
|
@@ -511,36 +490,16 @@ class OutputBuffer:
|
|
|
511
490
|
def flush(self) -> None:
|
|
512
491
|
"""Drop pending bundles and request a session-boundary reset.
|
|
513
492
|
|
|
514
|
-
The
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
sentinel is consumed, the next emission tick synthesises
|
|
519
|
-
``_create_black_bundle()`` with ``duplicate=True`` — the
|
|
520
|
-
correct pre-roll behaviour for a session boundary:
|
|
521
|
-
|
|
522
|
-
* the wire callback (``_send_out_app_bundle_sync``) drops
|
|
523
|
-
``duplicate=True`` outright;
|
|
524
|
-
* the recorder's ``mark_first_real_frame()`` latch is not
|
|
525
|
-
tripped (REA-2323 / #2325), so the recording timeline only
|
|
526
|
-
starts at the next real model frame.
|
|
527
|
-
|
|
528
|
-
Safe to call from any thread; in practice ``flush()`` is
|
|
529
|
-
called from the model thread (``model-run``), the same thread
|
|
530
|
-
that calls :meth:`submit`, so there is no submit/flush race
|
|
531
|
-
on the producer side either.
|
|
493
|
+
The emission thread performs the actual reset of
|
|
494
|
+
``_last_emitted`` when it dequeues the sentinel. Then the
|
|
495
|
+
next tick emits a fresh black bundle tagged ``is_fresh_black``
|
|
496
|
+
— forwarded by the wire, ignored by the recorder.
|
|
532
497
|
"""
|
|
533
498
|
self._drain_queue()
|
|
534
499
|
try:
|
|
535
500
|
self._q.put_nowait(_FLUSH_MARKER)
|
|
536
501
|
except queue.Full:
|
|
537
|
-
#
|
|
538
|
-
# only producer (submit) runs on the same thread as
|
|
539
|
-
# flush, so nothing can refill the queue between drain
|
|
540
|
-
# and put. Log loudly if it ever happens and continue —
|
|
541
|
-
# the next real frame from the new session will set
|
|
542
|
-
# _last_emitted correctly even if the sentinel never
|
|
543
|
-
# lands.
|
|
502
|
+
# Same thread as submit(), so unreachable in practice.
|
|
544
503
|
logger.warning(
|
|
545
504
|
"OutputBuffer.flush: queue full immediately after drain; "
|
|
546
505
|
"reset sentinel dropped"
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/session_recorder.py
RENAMED
|
@@ -250,10 +250,22 @@ class SessionRecorder:
|
|
|
250
250
|
# OutputBuffer integration
|
|
251
251
|
# ------------------------------------------------------------------
|
|
252
252
|
|
|
253
|
-
def _on_bundle_sync(
|
|
254
|
-
|
|
253
|
+
def _on_bundle_sync(
|
|
254
|
+
self,
|
|
255
|
+
bundle: MediaBundle,
|
|
256
|
+
duplicate: bool,
|
|
257
|
+
is_fresh_black: bool = False,
|
|
258
|
+
) -> None:
|
|
259
|
+
"""Enqueue a bundle for the feed worker; non-blocking.
|
|
260
|
+
|
|
261
|
+
Session-boundary synthesised black (``is_fresh_black``) is not
|
|
262
|
+
real model output and is invisible to the recording timeline:
|
|
263
|
+
no feed-queue write, no ``mark_first_real_frame`` consideration.
|
|
264
|
+
"""
|
|
255
265
|
if self._disabled or not self._started:
|
|
256
266
|
return
|
|
267
|
+
if is_fresh_black:
|
|
268
|
+
return
|
|
257
269
|
now = time.monotonic()
|
|
258
270
|
if duplicate:
|
|
259
271
|
if self._config.skip_leading_black and not self._markers.recording_started:
|
|
@@ -350,22 +350,19 @@ class Runtime(ModelStateMachine, ABC):
|
|
|
350
350
|
logger.warning("Failed to send app message", error=e)
|
|
351
351
|
|
|
352
352
|
def _send_out_app_bundle_sync(
|
|
353
|
-
self,
|
|
353
|
+
self,
|
|
354
|
+
media_bundle: MediaBundle,
|
|
355
|
+
duplicate: bool,
|
|
356
|
+
is_fresh_black: bool = False,
|
|
354
357
|
) -> None:
|
|
355
|
-
"""
|
|
356
|
-
|
|
357
|
-
Silently fails if the event loop is closed (expected during shutdown).
|
|
358
|
-
|
|
359
|
-
When *duplicate* is ``True`` the bundle is a re-emission of the
|
|
360
|
-
last frame (queue was empty, video-only). We skip forwarding
|
|
361
|
-
entirely: the video track already serves the last pushed frame,
|
|
362
|
-
and pushing the same frame again only wastes event-loop time.
|
|
358
|
+
"""OutputBuffer callback — forward real frames + session-boundary black.
|
|
363
359
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
Drops keepalive duplicates (the wire track already shows that
|
|
361
|
+
frame) but forwards ``is_fresh_black`` bundles so the client
|
|
362
|
+
transitions off the previous session's last frame on flush.
|
|
363
|
+
Silently fails if the event loop is closed (shutdown).
|
|
367
364
|
"""
|
|
368
|
-
if duplicate:
|
|
365
|
+
if duplicate and not is_fresh_black:
|
|
369
366
|
return
|
|
370
367
|
if self.loop.is_closed():
|
|
371
368
|
return
|
|
@@ -70,19 +70,26 @@ class HeadlessRuntime(Runtime):
|
|
|
70
70
|
# Runtime API implementation - MODEL -> CLIENT (stdout)
|
|
71
71
|
# ===============================
|
|
72
72
|
|
|
73
|
-
def _send_out_app_bundle_sync(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
def _send_out_app_bundle_sync(
|
|
74
|
+
self,
|
|
75
|
+
media_bundle,
|
|
76
|
+
duplicate: bool,
|
|
77
|
+
is_fresh_black: bool = False,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Forward every bundle to :meth:`send_out_app_bundle`.
|
|
80
|
+
|
|
81
|
+
Unlike the base class, headless does not drop duplicates here
|
|
82
|
+
— the async path counts them for the ``ignore_duplicates``
|
|
83
|
+
feature. Silently fails if the event loop is closed.
|
|
80
84
|
"""
|
|
81
85
|
if self.loop.is_closed():
|
|
82
86
|
return
|
|
83
87
|
try:
|
|
84
88
|
asyncio.run_coroutine_threadsafe(
|
|
85
|
-
self.send_out_app_bundle(
|
|
89
|
+
self.send_out_app_bundle(
|
|
90
|
+
media_bundle, duplicate, is_fresh_black=is_fresh_black
|
|
91
|
+
),
|
|
92
|
+
self.loop,
|
|
86
93
|
)
|
|
87
94
|
except RuntimeError:
|
|
88
95
|
pass
|
|
@@ -105,19 +112,30 @@ class HeadlessRuntime(Runtime):
|
|
|
105
112
|
output = json.dumps(data, indent=2)
|
|
106
113
|
print(f"\n[RUNTIME] {output}", flush=True)
|
|
107
114
|
|
|
108
|
-
async def send_out_app_bundle(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
async def send_out_app_bundle(
|
|
116
|
+
self,
|
|
117
|
+
media_bundle,
|
|
118
|
+
duplicate: bool,
|
|
119
|
+
*,
|
|
120
|
+
is_fresh_black: bool = False,
|
|
121
|
+
) -> None:
|
|
122
|
+
"""Extract the video frame from ``media_bundle`` and write as PNG.
|
|
123
|
+
|
|
124
|
+
Skips ``is_fresh_black`` bundles (synthesised session-boundary
|
|
125
|
+
black, not model output) and — when ``ignore_duplicates`` is
|
|
126
|
+
set — ``duplicate`` keepalives.
|
|
116
127
|
"""
|
|
117
128
|
if self.file_path is None:
|
|
118
129
|
return
|
|
119
130
|
|
|
120
|
-
|
|
131
|
+
if is_fresh_black:
|
|
132
|
+
self._duplicates_skipped += 1
|
|
133
|
+
logger.debug(
|
|
134
|
+
"Skipped fresh-black session-boundary frame",
|
|
135
|
+
total_skipped=self._duplicates_skipped,
|
|
136
|
+
)
|
|
137
|
+
return
|
|
138
|
+
|
|
121
139
|
if self.config.ignore_duplicates and duplicate:
|
|
122
140
|
self._duplicates_skipped += 1
|
|
123
141
|
logger.debug(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/driver/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/driver/step_result.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/connected.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/event.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/messages.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/events/upload.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/internal/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/decorators.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/handlers.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/model/reactor_model.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/pipeline/idle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/descriptors.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/input.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/interface/tracks/output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/base.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/file.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/backends/otlp.py
RENAMED
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/nvml_sampler.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/profiling/plotting/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/chunk_encoder.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/chunk_uploader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/recording/track_resolver.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/headless/config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/runtimes/http/http_runtime.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/serve/commands/schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/audio_track.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/aiortc/video_track.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/__init__.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/gst.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/sdp/ice.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/settings.py
RENAMED
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime/transports/gstreamer/signals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reactor_runtime-2.7.6 → reactor_runtime-2.7.7}/src/reactor_runtime.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|