rocket-welder-sdk 1.1.38__tar.gz → 1.1.38.dev18__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.
Files changed (68) hide show
  1. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/PKG-INFO +1 -1
  2. rocket_welder_sdk-1.1.38.dev18/VERSION +1 -0
  3. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/__init__.py +0 -20
  4. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/controllers.py +0 -7
  5. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/gst_metadata.py +4 -48
  6. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/frame_sink_factory.py +2 -2
  7. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/rocket_welder_client.py +0 -202
  8. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/segmentation_result.py +0 -132
  9. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/unix_socket_transport.py +1 -51
  10. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk.egg-info/PKG-INFO +1 -1
  11. rocket_welder_sdk-1.1.38/VERSION +0 -1
  12. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/MANIFEST.in +0 -0
  13. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/README.md +0 -0
  14. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/logo.png +0 -0
  15. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/pyproject.toml +0 -0
  16. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/bytes_size.py +0 -0
  17. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/connection_string.py +0 -0
  18. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/external_controls/__init__.py +0 -0
  19. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/external_controls/contracts.py +0 -0
  20. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/external_controls/contracts_old.py +0 -0
  21. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/frame_metadata.py +0 -0
  22. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/__init__.py +0 -0
  23. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/client.py +0 -0
  24. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/connection_strings.py +0 -0
  25. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/data_context.py +0 -0
  26. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/schema.py +0 -0
  27. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/high_level/transport_protocol.py +0 -0
  28. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/keypoints_protocol.py +0 -0
  29. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/opencv_controller.py +0 -0
  30. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/periodic_timer.py +0 -0
  31. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/py.typed +0 -0
  32. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/session_id.py +0 -0
  33. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/__init__.py +0 -0
  34. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/frame_sink.py +0 -0
  35. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/frame_source.py +0 -0
  36. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/nng_transport.py +0 -0
  37. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/stream_transport.py +0 -0
  38. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/transport/tcp_transport.py +0 -0
  39. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/__init__.py +0 -0
  40. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/controls.py +0 -0
  41. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/icons.py +0 -0
  42. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/ui_events_projection.py +0 -0
  43. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/ui_service.py +0 -0
  44. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk/ui/value_types.py +0 -0
  45. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk.egg-info/SOURCES.txt +0 -0
  46. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk.egg-info/dependency_links.txt +0 -0
  47. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk.egg-info/requires.txt +0 -0
  48. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/rocket_welder_sdk.egg-info/top_level.txt +0 -0
  49. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/setup.cfg +0 -0
  50. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/setup.py +0 -0
  51. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_bytes_size.py +0 -0
  52. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_connection_string.py +0 -0
  53. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_controllers.py +0 -0
  54. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_external_controls_serialization.py +0 -0
  55. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_external_controls_serialization_v2.py +0 -0
  56. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_frame_metadata.py +0 -0
  57. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_gst_metadata.py +0 -0
  58. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_high_level_api.py +0 -0
  59. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_icons.py +0 -0
  60. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_keypoints_cross_platform.py +0 -0
  61. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_keypoints_protocol.py +0 -0
  62. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_rocket_welder_client.py +0 -0
  63. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_segmentation_cross_platform.py +0 -0
  64. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_segmentation_result.py +0 -0
  65. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_session_id.py +0 -0
  66. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_transport_cross_platform.py +0 -0
  67. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_ui_controls.py +0 -0
  68. {rocket_welder_sdk-1.1.38 → rocket_welder_sdk-1.1.38.dev18}/tests/test_ui_service_happy_path.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.38
3
+ Version: 1.1.38.dev18
4
4
  Summary: High-performance video streaming SDK for RocketWelder services using ZeroBuffer IPC
5
5
  Home-page: https://github.com/modelingevolution/rocket-welder-sdk
6
6
  Author: ModelingEvolution
@@ -0,0 +1 @@
1
+ 1.1.38.dev18
@@ -12,21 +12,9 @@ from .connection_string import ConnectionMode, ConnectionString, Protocol
12
12
  from .controllers import DuplexShmController, IController, OneWayShmController
13
13
  from .frame_metadata import FRAME_METADATA_SIZE, FrameMetadata, GstVideoFormat
14
14
  from .gst_metadata import GstCaps, GstMetadata
15
- from .keypoints_protocol import (
16
- IKeyPointsSink,
17
- IKeyPointsWriter,
18
- KeyPointsSink,
19
- KeyPointsWriter,
20
- )
21
15
  from .opencv_controller import OpenCvController
22
16
  from .periodic_timer import PeriodicTimer, PeriodicTimerSync
23
17
  from .rocket_welder_client import RocketWelderClient
24
- from .segmentation_result import (
25
- ISegmentationResultSink,
26
- ISegmentationResultWriter,
27
- SegmentationResultSink,
28
- SegmentationResultWriter,
29
- )
30
18
  from .session_id import (
31
19
  # Explicit URL functions (PREFERRED - set by rocket-welder2)
32
20
  ACTIONS_SINK_URL_ENV,
@@ -86,20 +74,12 @@ __all__ = [
86
74
  "GstMetadata",
87
75
  "GstVideoFormat",
88
76
  "IController",
89
- "IKeyPointsSink",
90
- "IKeyPointsWriter",
91
- "ISegmentationResultSink",
92
- "ISegmentationResultWriter",
93
- "KeyPointsSink",
94
- "KeyPointsWriter",
95
77
  "OneWayShmController",
96
78
  "OpenCvController",
97
79
  "PeriodicTimer",
98
80
  "PeriodicTimerSync",
99
81
  "Protocol",
100
82
  "RocketWelderClient",
101
- "SegmentationResultSink",
102
- "SegmentationResultWriter",
103
83
  "get_actions_url",
104
84
  "get_actions_url_from_env",
105
85
  "get_configured_nng_urls",
@@ -758,13 +758,6 @@ class DuplexShmController(IController):
758
758
 
759
759
  # GstCaps must be available for width/height/format
760
760
  # (FrameMetadata no longer contains these - they're stream-level, not per-frame)
761
- # If not available yet, try to read metadata again (race condition with C# Server)
762
- if not self._gst_caps and self._duplex_server and self._duplex_server.request_reader:
763
- logger.debug("GstCaps not available, attempting to read metadata again...")
764
- metadata = self._duplex_server.request_reader.get_metadata()
765
- if metadata:
766
- self._on_metadata(metadata)
767
-
768
761
  if not self._gst_caps:
769
762
  logger.warning(
770
763
  "GstCaps not available, skipping frame %d", frame_metadata.frame_number
@@ -54,14 +54,11 @@ class GstCaps:
54
54
  @classmethod
55
55
  def parse(cls, caps_string: str) -> GstCaps:
56
56
  """
57
- Parse GStreamer caps string or simple format string.
58
-
59
- Supports two formats:
60
- 1. Full GStreamer format: "video/x-raw, format=(string)RGB, width=(int)640, height=(int)480"
61
- 2. Simple format: "640x480 RGB" or "640x480 RGB @ 30.00fps"
57
+ Parse GStreamer caps string.
58
+ Example: "video/x-raw, format=(string)RGB, width=(int)640, height=(int)480, framerate=(fraction)30/1"
62
59
 
63
60
  Args:
64
- caps_string: Caps string in either format
61
+ caps_string: GStreamer caps string
65
62
 
66
63
  Returns:
67
64
  GstCaps instance
@@ -74,50 +71,9 @@ class GstCaps:
74
71
 
75
72
  caps_string = caps_string.strip()
76
73
 
77
- # Try simple format first: "WIDTHxHEIGHT FORMAT" or "WIDTHxHEIGHT FORMAT @ FPS"
78
- # Example: "320x240 BGR" or "1920x1080 RGB @ 30.00fps"
79
- simple_match = re.match(r"^(\d+)x(\d+)\s+(\w+)(?:\s*@\s*([\d.]+)\s*fps)?$", caps_string)
80
- if simple_match:
81
- width = int(simple_match.group(1))
82
- height = int(simple_match.group(2))
83
- format_str = simple_match.group(3)
84
- fps_str = simple_match.group(4)
85
-
86
- framerate_num = None
87
- framerate_den = None
88
- if fps_str:
89
- # Convert float FPS to fraction
90
- fps = float(fps_str)
91
- # Use common framerates or default to fps/1
92
- if abs(fps - 30.0) < 0.01:
93
- framerate_num, framerate_den = 30, 1
94
- elif abs(fps - 29.97) < 0.01:
95
- framerate_num, framerate_den = 30000, 1001
96
- elif abs(fps - 25.0) < 0.01:
97
- framerate_num, framerate_den = 25, 1
98
- elif abs(fps - 60.0) < 0.01:
99
- framerate_num, framerate_den = 60, 1
100
- elif abs(fps - 59.94) < 0.01:
101
- framerate_num, framerate_den = 60000, 1001
102
- else:
103
- framerate_num, framerate_den = int(fps * 1000), 1000
104
-
105
- depth_type, channels, bytes_per_pixel = cls._map_gstreamer_format_to_numpy(format_str)
106
- return cls(
107
- width=width,
108
- height=height,
109
- format=format_str,
110
- depth_type=depth_type,
111
- channels=channels,
112
- bytes_per_pixel=bytes_per_pixel,
113
- framerate_num=framerate_num,
114
- framerate_den=framerate_den,
115
- caps_string=None, # Not a real GStreamer caps string
116
- )
117
-
118
74
  # Check if it's a video caps
119
75
  if not caps_string.startswith("video/x-raw"):
120
- raise ValueError(f"Not a video/x-raw caps string or simple format: {caps_string}")
76
+ raise ValueError(f"Not a video/x-raw caps string: {caps_string}")
121
77
 
122
78
  try:
123
79
  # Parse width
@@ -84,8 +84,8 @@ class FrameSinkFactory:
84
84
  return StreamFrameSink(file_handle)
85
85
 
86
86
  if protocol.is_socket:
87
- log.info("Creating Unix socket frame sink (server/bind) at: %s", address)
88
- return UnixSocketFrameSink.bind(address)
87
+ log.info("Creating Unix socket frame sink at: %s", address)
88
+ return UnixSocketFrameSink.connect(address)
89
89
 
90
90
  if protocol.is_nng:
91
91
  log.info("Creating NNG frame sink (%s) at: %s", protocol.schema, address)
@@ -15,15 +15,7 @@ import numpy as np
15
15
  from .connection_string import ConnectionMode, ConnectionString, Protocol
16
16
  from .controllers import DuplexShmController, IController, OneWayShmController
17
17
  from .frame_metadata import FrameMetadata # noqa: TC001 - used at runtime in callbacks
18
- from .high_level.connection_strings import KeyPointsConnectionString, SegmentationConnectionString
19
- from .high_level.frame_sink_factory import FrameSinkFactory
20
- from .keypoints_protocol import IKeyPointsSink, IKeyPointsWriter, KeyPointsSink
21
18
  from .opencv_controller import OpenCvController
22
- from .segmentation_result import (
23
- ISegmentationResultSink,
24
- ISegmentationResultWriter,
25
- SegmentationResultSink,
26
- )
27
19
  from .session_id import (
28
20
  get_configured_nng_urls,
29
21
  get_nng_urls_from_env,
@@ -254,144 +246,6 @@ class RocketWelderClient:
254
246
  self._controller.start(actual_callback, cancellation_token) # type: ignore[arg-type]
255
247
  logger.info("RocketWelder client started with %s", self._connection)
256
248
 
257
- def start_with_writers(
258
- self,
259
- on_frame: Callable[[Mat, ISegmentationResultWriter, IKeyPointsWriter, Mat], None], # type: ignore[valid-type]
260
- cancellation_token: Optional[threading.Event] = None,
261
- ) -> None:
262
- """
263
- Start receiving frames with segmentation and keypoints output support.
264
-
265
- Creates sinks for streaming AI results to rocket-welder2.
266
-
267
- Configuration via environment variables:
268
- - SEGMENTATION_SINK_URL: URL for segmentation output (e.g., socket:///tmp/seg.sock)
269
- - KEYPOINTS_SINK_URL: URL for keypoints output (e.g., socket:///tmp/kp.sock)
270
-
271
- Args:
272
- on_frame: Callback receiving (input_mat, seg_writer, kp_writer, output_mat).
273
- The writers are created per-frame and auto-flush on context exit.
274
- cancellation_token: Optional cancellation token
275
-
276
- Example:
277
- def process_frame(input_mat, seg_writer, kp_writer, output_mat):
278
- # Run AI inference
279
- result = ai_model.infer(input_mat)
280
-
281
- # Write segmentation results
282
- for instance in result.instances:
283
- seg_writer.append(instance.class_id, instance.instance_id, instance.points)
284
-
285
- # Write keypoints
286
- for kp in result.keypoints:
287
- kp_writer.append(kp.id, kp.x, kp.y, kp.confidence)
288
-
289
- # Copy/draw to output
290
- output_mat[:] = input_mat
291
-
292
- client.start_with_writers(process_frame)
293
-
294
- Raises:
295
- RuntimeError: If already running
296
- ValueError: If connection type is not supported or not duplex mode
297
- """
298
- with self._lock:
299
- if self._controller and self._controller.is_running:
300
- raise RuntimeError("Client is already running")
301
-
302
- # This overload requires duplex mode
303
- if self._connection.connection_mode != ConnectionMode.DUPLEX:
304
- raise ValueError("start_with_writers() requires duplex connection mode")
305
-
306
- # Create controller
307
- if self._connection.protocol == Protocol.SHM:
308
- self._controller = DuplexShmController(self._connection)
309
- elif self._connection.protocol == Protocol.FILE or bool(
310
- self._connection.protocol & Protocol.MJPEG # type: ignore[operator]
311
- ):
312
- self._controller = OpenCvController(self._connection)
313
- else:
314
- raise ValueError(f"Unsupported protocol: {self._connection.protocol}")
315
-
316
- # Create sinks from environment
317
- seg_sink = self._get_or_create_segmentation_sink()
318
- kp_sink = self._get_or_create_keypoints_sink()
319
-
320
- logger.info(
321
- "Starting RocketWelder client with AI output support: seg=%s, kp=%s",
322
- "configured" if seg_sink else "null",
323
- "configured" if kp_sink else "null",
324
- )
325
-
326
- # Wrapper callback that creates per-frame writers
327
- def writer_callback(
328
- frame_metadata: FrameMetadata, input_mat: Mat, output_mat: Mat # type: ignore[valid-type]
329
- ) -> None:
330
- # Get caps from controller metadata (width/height for segmentation)
331
- metadata = self._controller.get_metadata() if self._controller else None
332
- caps = metadata.caps if metadata else None
333
-
334
- if caps is None:
335
- logger.warning(
336
- "GstCaps not available for frame %d, using no-op writers",
337
- frame_metadata.frame_number,
338
- )
339
- # Use no-op writers
340
- on_frame(
341
- input_mat, _NoOpSegmentationWriter(), _NoOpKeyPointsWriter(), output_mat
342
- )
343
- return
344
-
345
- # Create per-frame writers from sinks
346
- with seg_sink.create_writer(
347
- frame_metadata.frame_number, caps.width, caps.height
348
- ) as seg_writer, kp_sink.create_writer(frame_metadata.frame_number) as kp_writer:
349
- # Call user callback with writers
350
- on_frame(input_mat, seg_writer, kp_writer, output_mat)
351
- # Writers auto-flush on context exit
352
-
353
- # Start the controller with our wrapper
354
- self._controller.start(writer_callback, cancellation_token) # type: ignore[arg-type]
355
- logger.info("RocketWelder client started with writers: %s", self._connection)
356
-
357
- def _get_or_create_segmentation_sink(self) -> ISegmentationResultSink:
358
- """Get or create segmentation result sink from environment."""
359
- import os
360
-
361
- url = os.environ.get("SEGMENTATION_SINK_URL")
362
- if not url:
363
- logger.debug("SEGMENTATION_SINK_URL not set, using null sink")
364
- return _NullSegmentationSink()
365
-
366
- try:
367
- cs = SegmentationConnectionString.parse(url)
368
- frame_sink = FrameSinkFactory.create(cs.protocol, cs.address)
369
- return SegmentationResultSink(frame_sink=frame_sink, owns_sink=True)
370
- except Exception as ex:
371
- logger.warning("Failed to create segmentation sink from %s: %s", url, ex)
372
- return _NullSegmentationSink()
373
-
374
- def _get_or_create_keypoints_sink(self) -> IKeyPointsSink:
375
- """Get or create keypoints sink from environment."""
376
- import os
377
-
378
- url = os.environ.get("KEYPOINTS_SINK_URL")
379
- if not url:
380
- logger.debug("KEYPOINTS_SINK_URL not set, using null sink")
381
- return _NullKeyPointsSink()
382
-
383
- try:
384
- cs = KeyPointsConnectionString.parse(url)
385
- frame_sink = FrameSinkFactory.create(cs.protocol, cs.address)
386
- return KeyPointsSink(
387
- frame_sink=frame_sink,
388
- master_frame_interval=cs.master_frame_interval,
389
- owns_sink=True,
390
- )
391
- except Exception as ex:
392
- logger.warning("Failed to create keypoints sink from %s: %s", url, ex)
393
- return _NullKeyPointsSink()
394
-
395
249
  def stop(self) -> None:
396
250
  """Stop the client and clean up resources."""
397
251
  with self._lock:
@@ -641,59 +495,3 @@ class RocketWelderClient:
641
495
  f"shm://{buffer_name}?size={buffer_size}&metadata={metadata_size}&mode=Duplex"
642
496
  )
643
497
  return cls(connection_str)
644
-
645
-
646
- # No-op implementations for when sinks are not configured
647
-
648
-
649
- class _NoOpKeyPointsWriter(IKeyPointsWriter):
650
- """No-op keypoints writer that discards all data."""
651
-
652
- def append(self, keypoint_id: int, x: int, y: int, confidence: float) -> None:
653
- """Discard keypoint data."""
654
- pass
655
-
656
- def append_point(self, keypoint_id: int, point: tuple, confidence: float) -> None: # type: ignore[type-arg]
657
- """Discard keypoint data."""
658
- pass
659
-
660
- def close(self) -> None:
661
- """No-op close."""
662
- pass
663
-
664
-
665
- class _NoOpSegmentationWriter(ISegmentationResultWriter):
666
- """No-op segmentation writer that discards all data."""
667
-
668
- def append(self, class_id: int, instance_id: int, points: Any) -> None:
669
- """Discard segmentation data."""
670
- pass
671
-
672
- def close(self) -> None:
673
- """No-op close."""
674
- pass
675
-
676
-
677
- class _NullKeyPointsSink(IKeyPointsSink):
678
- """Null keypoints sink that creates no-op writers."""
679
-
680
- def create_writer(self, frame_id: int) -> IKeyPointsWriter:
681
- """Create a no-op writer."""
682
- return _NoOpKeyPointsWriter()
683
-
684
- @staticmethod
685
- def read(json_definition: str, blob_stream: Any) -> Any:
686
- """Not supported for null sink."""
687
- raise NotImplementedError("NullKeyPointsSink does not support reading")
688
-
689
-
690
- class _NullSegmentationSink(ISegmentationResultSink):
691
- """Null segmentation sink that creates no-op writers."""
692
-
693
- def create_writer(self, frame_id: int, width: int, height: int) -> ISegmentationResultWriter:
694
- """Create a no-op writer."""
695
- return _NoOpSegmentationWriter()
696
-
697
- def close(self) -> None:
698
- """No-op close."""
699
- pass
@@ -21,7 +21,6 @@ Features:
21
21
 
22
22
  import io
23
23
  import struct
24
- from abc import ABC, abstractmethod
25
24
  from dataclasses import dataclass
26
25
  from typing import BinaryIO, Iterator, List, Optional, Tuple, Union
27
26
 
@@ -419,134 +418,3 @@ class SegmentationResultReader:
419
418
  def __exit__(self, *args: object) -> None:
420
419
  """Context manager exit."""
421
420
  pass
422
-
423
-
424
- class ISegmentationResultWriter(ABC):
425
- """Interface for writing segmentation results for a single frame."""
426
-
427
- @abstractmethod
428
- def append(
429
- self,
430
- class_id: int,
431
- instance_id: int,
432
- points: Union[List[Point], PointArray],
433
- ) -> None:
434
- """
435
- Append an instance with contour points.
436
-
437
- Args:
438
- class_id: Object class ID (0-255)
439
- instance_id: Instance ID within class (0-255)
440
- points: List of (x, y) tuples or NumPy array of shape (N, 2)
441
- """
442
- pass
443
-
444
- @abstractmethod
445
- def close(self) -> None:
446
- """Flush and close the writer."""
447
- pass
448
-
449
- def __enter__(self) -> "ISegmentationResultWriter":
450
- """Context manager entry."""
451
- return self
452
-
453
- def __exit__(self, *args: object) -> None:
454
- """Context manager exit."""
455
- self.close()
456
-
457
-
458
- class ISegmentationResultSink(ABC):
459
- """
460
- Factory for creating segmentation result writers per frame (transport-agnostic).
461
-
462
- Mirrors C# ISegmentationResultSink interface.
463
- """
464
-
465
- @abstractmethod
466
- def create_writer(self, frame_id: int, width: int, height: int) -> ISegmentationResultWriter:
467
- """
468
- Create a writer for the current frame.
469
-
470
- Args:
471
- frame_id: Unique frame identifier
472
- width: Frame width in pixels
473
- height: Frame height in pixels
474
-
475
- Returns:
476
- Segmentation result writer for this frame
477
- """
478
- pass
479
-
480
- @abstractmethod
481
- def close(self) -> None:
482
- """Close the sink and release resources."""
483
- pass
484
-
485
- def __enter__(self) -> "ISegmentationResultSink":
486
- """Context manager entry."""
487
- return self
488
-
489
- def __exit__(self, *args: object) -> None:
490
- """Context manager exit."""
491
- self.close()
492
-
493
-
494
- class SegmentationResultSink(ISegmentationResultSink):
495
- """
496
- Transport-agnostic segmentation result sink.
497
-
498
- Creates writers for each frame that serialize to the underlying IFrameSink.
499
-
500
- Thread-safe: No (caller must synchronize)
501
- """
502
-
503
- def __init__(
504
- self,
505
- stream: Optional[BinaryIO] = None,
506
- *,
507
- frame_sink: Optional[IFrameSink] = None,
508
- owns_sink: bool = False,
509
- ) -> None:
510
- """
511
- Initialize segmentation result sink.
512
-
513
- Args:
514
- stream: BinaryIO stream (convenience - auto-wraps in StreamFrameSink)
515
- frame_sink: IFrameSink to write frames to (keyword-only, transport-agnostic)
516
- owns_sink: If True, closes the sink on disposal (keyword-only)
517
-
518
- Note:
519
- Either stream or frame_sink must be provided (not both).
520
- For convenience, stream is the primary parameter (auto-wraps in StreamFrameSink).
521
- For transport-agnostic usage, use frame_sink= keyword argument.
522
- """
523
- if frame_sink is None and stream is None:
524
- raise TypeError("Either stream or frame_sink must be provided")
525
-
526
- if frame_sink is not None and stream is not None:
527
- raise TypeError("Cannot provide both stream and frame_sink")
528
-
529
- # Convenience: auto-wrap stream in StreamFrameSink
530
- if stream is not None:
531
- self._frame_sink: IFrameSink = StreamFrameSink(stream, leave_open=False)
532
- self._owns_sink = True
533
- else:
534
- assert frame_sink is not None
535
- self._frame_sink = frame_sink
536
- self._owns_sink = owns_sink
537
-
538
- def create_writer(self, frame_id: int, width: int, height: int) -> ISegmentationResultWriter:
539
- """Create a writer for the current frame."""
540
- # SegmentationResultWriter implements the write methods we need
541
- # We return it as ISegmentationResultWriter
542
- return SegmentationResultWriter( # type: ignore[return-value]
543
- frame_id=frame_id,
544
- width=width,
545
- height=height,
546
- frame_sink=self._frame_sink,
547
- )
548
-
549
- def close(self) -> None:
550
- """Close the sink and release resources."""
551
- if self._owns_sink:
552
- self._frame_sink.close()
@@ -22,26 +22,19 @@ class UnixSocketFrameSink(IFrameSink):
22
22
  Each frame is prefixed with a 4-byte little-endian length header.
23
23
  """
24
24
 
25
- def __init__(
26
- self,
27
- sock: socket.socket,
28
- leave_open: bool = False,
29
- server: Optional["UnixSocketServer"] = None,
30
- ):
25
+ def __init__(self, sock: socket.socket, leave_open: bool = False):
31
26
  """
32
27
  Create a Unix socket frame sink.
33
28
 
34
29
  Args:
35
30
  sock: Connected Unix domain socket
36
31
  leave_open: If True, doesn't close socket on close
37
- server: Optional server to clean up on close (used by bind())
38
32
  """
39
33
  if sock.family != socket.AF_UNIX:
40
34
  raise ValueError("Socket must be a Unix domain socket")
41
35
 
42
36
  self._socket = sock
43
37
  self._leave_open = leave_open
44
- self._server = server
45
38
  self._closed = False
46
39
 
47
40
  @classmethod
@@ -76,45 +69,6 @@ class UnixSocketFrameSink(IFrameSink):
76
69
  await loop.sock_connect(sock, socket_path)
77
70
  return cls(sock, leave_open=False)
78
71
 
79
- @classmethod
80
- def bind(cls, socket_path: str) -> "UnixSocketFrameSink":
81
- """
82
- Bind to a Unix socket path as a server and wait for a client to connect.
83
-
84
- Use this when the SDK is the producer (server) and rocket-welder2 is
85
- the consumer (client).
86
-
87
- Args:
88
- socket_path: Path to Unix socket file
89
-
90
- Returns:
91
- Frame sink connected to the first client
92
-
93
- Note:
94
- This is the server-side counterpart to connect().
95
- The server binds and listens, then blocks until a client connects.
96
- """
97
- server = UnixSocketServer(socket_path)
98
- server.start()
99
- client_socket = server.accept()
100
- return cls(client_socket, leave_open=False, server=server)
101
-
102
- @classmethod
103
- async def bind_async(cls, socket_path: str) -> "UnixSocketFrameSink":
104
- """
105
- Bind to a Unix socket path as a server and wait asynchronously for a client.
106
-
107
- Args:
108
- socket_path: Path to Unix socket file
109
-
110
- Returns:
111
- Frame sink connected to the first client
112
- """
113
- server = UnixSocketServer(socket_path)
114
- server.start()
115
- client_socket = await server.accept_async()
116
- return cls(client_socket, leave_open=False, server=server)
117
-
118
72
  def write_frame(self, frame_data: bytes) -> None:
119
73
  """Write frame with 4-byte length prefix to Unix socket."""
120
74
  if self._closed:
@@ -158,10 +112,6 @@ class UnixSocketFrameSink(IFrameSink):
158
112
  with contextlib.suppress(OSError):
159
113
  self._socket.shutdown(socket.SHUT_WR)
160
114
  self._socket.close()
161
- # Clean up server if we created one via bind()
162
- if self._server is not None:
163
- self._server.stop()
164
- self._server = None
165
115
 
166
116
  async def close_async(self) -> None:
167
117
  """Close the Unix socket sink asynchronously."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.38
3
+ Version: 1.1.38.dev18
4
4
  Summary: High-performance video streaming SDK for RocketWelder services using ZeroBuffer IPC
5
5
  Home-page: https://github.com/modelingevolution/rocket-welder-sdk
6
6
  Author: ModelingEvolution
@@ -1 +0,0 @@
1
- 1.1.38