rocket-welder-sdk 0.0.1__py3-none-any.whl → 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,8 @@
1
+ """
2
+ Rocket Welder SDK - Python client library for RocketWelder video streaming services.
3
+ """
4
+
5
+ from .client import Client
6
+
7
+ __version__ = "1.0.0"
8
+ __all__ = ["Client"]
@@ -0,0 +1,183 @@
1
+ """
2
+ RocketWelder client implementation.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import time
8
+ import threading
9
+ from typing import Optional, Callable, Dict, Any, Iterator
10
+ import numpy as np
11
+ import cv2
12
+
13
+
14
+ class Client:
15
+ """Client for RocketWelder video streaming services."""
16
+
17
+ def __init__(self, connection_string: str):
18
+ """
19
+ Initialize client with connection string.
20
+
21
+ Args:
22
+ connection_string: Connection string (e.g., "shm://buffer_name")
23
+ """
24
+ self.connection_string = connection_string
25
+ self._callback: Optional[Callable[[np.ndarray], None]] = None
26
+ self._callback_with_metadata: Optional[Callable[[np.ndarray, Dict[str, Any]], None]] = None
27
+ self._running = False
28
+ self._thread: Optional[threading.Thread] = None
29
+
30
+ # TODO: Parse connection string
31
+ # TODO: Initialize based on protocol (shm://, mjpeg+http://, etc.)
32
+
33
+ @classmethod
34
+ def from_args(cls, argv: list) -> 'Client':
35
+ """
36
+ Create client from command line arguments and environment variables.
37
+ Environment variable CONNECTION_STRING is checked first, then overridden by args.
38
+
39
+ Args:
40
+ argv: Command line arguments (typically sys.argv)
41
+
42
+ Returns:
43
+ Client instance
44
+ """
45
+ # Check environment variable first
46
+ connection_string = os.environ.get('CONNECTION_STRING')
47
+
48
+ # Override with command line args if present
49
+ if argv:
50
+ for arg in argv[1:]: # Skip program name
51
+ if (arg.startswith('shm://') or
52
+ arg.startswith('mjpeg+http://') or
53
+ arg.startswith('mjpeg+tcp://')):
54
+ connection_string = arg
55
+ break
56
+
57
+ return cls(connection_string or 'shm://default')
58
+
59
+ @classmethod
60
+ def from_env(cls) -> 'Client':
61
+ """
62
+ Create client from environment variable CONNECTION_STRING.
63
+
64
+ Returns:
65
+ Client instance
66
+ """
67
+ connection_string = os.environ.get('CONNECTION_STRING', 'shm://default')
68
+ return cls(connection_string)
69
+
70
+ @classmethod
71
+ def from_connection_string(cls, connection_string: str) -> 'Client':
72
+ """
73
+ Create client from a specific connection string.
74
+
75
+ Args:
76
+ connection_string: Connection string
77
+
78
+ Returns:
79
+ Client instance
80
+ """
81
+ return cls(connection_string)
82
+
83
+ def on_frame(self, callback: Callable) -> Callable:
84
+ """
85
+ Decorator/method to set frame processing callback.
86
+
87
+ The callback can have one of these signatures:
88
+ - callback(frame: np.ndarray) -> None
89
+ - callback(frame: np.ndarray, metadata: dict) -> None
90
+
91
+ Args:
92
+ callback: Function to process frames
93
+
94
+ Returns:
95
+ The callback function (for decorator usage)
96
+ """
97
+ import inspect
98
+ sig = inspect.signature(callback)
99
+ param_count = len(sig.parameters)
100
+
101
+ if param_count == 1:
102
+ self._callback = callback
103
+ elif param_count == 2:
104
+ self._callback_with_metadata = callback
105
+ else:
106
+ raise ValueError(f"Callback must have 1 or 2 parameters, got {param_count}")
107
+
108
+ return callback
109
+
110
+ def start(self):
111
+ """Start frame processing."""
112
+ if self._running:
113
+ return
114
+
115
+ if not self._callback and not self._callback_with_metadata:
116
+ raise RuntimeError("Frame callback must be set before starting")
117
+
118
+ self._running = True
119
+ self._thread = threading.Thread(target=self._process_frames)
120
+ self._thread.daemon = True
121
+ self._thread.start()
122
+
123
+ def stop(self):
124
+ """Stop frame processing."""
125
+ if not self._running:
126
+ return
127
+
128
+ self._running = False
129
+ if self._thread:
130
+ self._thread.join(timeout=5.0)
131
+ self._thread = None
132
+
133
+ def is_running(self) -> bool:
134
+ """Check if client is running."""
135
+ return self._running
136
+
137
+ def frames(self) -> Iterator[np.ndarray]:
138
+ """
139
+ Iterator interface for frame processing.
140
+
141
+ Yields:
142
+ Frame as numpy array
143
+ """
144
+ # TODO: Implement actual frame reading
145
+ # For now, generate dummy frames for testing
146
+ while True:
147
+ # Create dummy frame
148
+ frame = np.zeros((480, 640, 3), dtype=np.uint8)
149
+ yield frame
150
+ time.sleep(0.033) # ~30 FPS
151
+
152
+ def _process_frames(self):
153
+ """Internal method to process frames in thread."""
154
+ # TODO: Implement actual frame processing
155
+ # For now, generate dummy frames for testing
156
+ while self._running:
157
+ # Create dummy frame
158
+ frame = np.zeros((480, 640, 3), dtype=np.uint8)
159
+
160
+ # Create dummy metadata
161
+ metadata = {
162
+ 'timestamp': time.time(),
163
+ 'format': 'BGR',
164
+ 'width': 640,
165
+ 'height': 480
166
+ }
167
+
168
+ # Call appropriate callback
169
+ if self._callback:
170
+ self._callback(frame)
171
+ elif self._callback_with_metadata:
172
+ self._callback_with_metadata(frame, metadata)
173
+
174
+ time.sleep(0.033) # ~30 FPS
175
+
176
+ def __enter__(self):
177
+ """Context manager entry."""
178
+ self.start()
179
+ return self
180
+
181
+ def __exit__(self, exc_type, exc_val, exc_tb):
182
+ """Context manager exit."""
183
+ self.stop()
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: rocket-welder-sdk
3
+ Version: 1.0.0
4
+ Summary: Client library for RocketWelder video streaming services
5
+ Home-page: https://github.com/modelingevolution/rocket-welder-sdk
6
+ Author: RocketWelder
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Topic :: Multimedia :: Video
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: numpy>=1.20.0
20
+ Requires-Dist: opencv-python>=4.5.0
21
+ Requires-Dist: zerobuffer-ipc>=1.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0; extra == "dev"
24
+ Requires-Dist: black>=22.0; extra == "dev"
25
+ Requires-Dist: mypy>=1.0; extra == "dev"
26
+ Dynamic: author
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: provides-extra
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
35
+
36
+ Client library for RocketWelder video streaming services
@@ -0,0 +1,6 @@
1
+ rocket_welder_sdk/__init__.py,sha256=LT2gnntM29r0IaokaJYOBOIccEcIYZm6FU_t9Eg1pzY,164
2
+ rocket_welder_sdk/client.py,sha256=W4WZdzAJ5eHuDUxQZyR_2rI9i1w2Zxr-UfwcFzKOwDw,5686
3
+ rocket_welder_sdk-1.0.0.dist-info/METADATA,sha256=_cgAcpZzxxEpx8LXVSEzby3YLS6YwohB40zcCkl0CYU,1264
4
+ rocket_welder_sdk-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ rocket_welder_sdk-1.0.0.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
6
+ rocket_welder_sdk-1.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.0.4)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1 @@
1
+ rocket_welder_sdk
File without changes
@@ -1,76 +0,0 @@
1
- # rocket_welder_camera/camera.py
2
- import cv2
3
- import threading
4
- import socket
5
- import struct
6
- import numpy as np
7
- from urllib.parse import urlparse
8
-
9
- class RocketWelderCamera:
10
- def __init__(self, url):
11
- self.url = url
12
- parsed_url = urlparse(url)
13
- self.host = parsed_url.hostname
14
- self.port = parsed_url.port
15
- self.stream_name = parsed_url.path[1:]
16
- self.buffer_size = 10
17
- self.circular_buffer = [None] * self.buffer_size
18
- self.current_frame = -1
19
- self.lock = threading.Lock()
20
- self.thread = threading.Thread(target=self.receive_frames)
21
- self.thread.daemon = True
22
- self.thread.start()
23
-
24
- @staticmethod
25
- def write_prefixed_ascii_string(socket, value):
26
- # Encode the string to ASCII bytes
27
- name = value.encode('ascii')
28
-
29
- # Send the length of the string as a single byte
30
- length_byte = len(name).to_bytes(1, 'little')
31
- socket.send(length_byte)
32
-
33
- # Send the actual ASCII bytes
34
- socket.send(name)
35
-
36
- def receive_frames(self):
37
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
38
- sock.connect((self.host, self.port))
39
- self.write_prefixed_ascii_string(sock,self.stream_name)
40
-
41
- while True:
42
- header_data = sock.recv(32)
43
- if len(header_data) != 32:
44
- continue
45
-
46
- frame_number, frame_size, stream_position, xor = struct.unpack('<QQQQ', header_data)
47
- xor_check = frame_number ^ frame_size ^ stream_position
48
- if frame_size == 0 or xor_check != xor:
49
- continue
50
-
51
- frame_size = int(frame_size)
52
- frame_data = b''
53
-
54
- while len(frame_data) < frame_size:
55
- packet = sock.recv(frame_size - len(frame_data))
56
- if not packet:
57
- break
58
- frame_data += packet
59
-
60
- if len(frame_data) != frame_size:
61
- continue
62
-
63
- frame = np.frombuffer(frame_data, dtype=np.uint8)
64
- image = cv2.imdecode(frame, cv2.IMREAD_COLOR)
65
-
66
- with self.lock:
67
- self.current_frame = (self.current_frame + 1) % self.buffer_size
68
- self.circular_buffer[self.current_frame] = image
69
-
70
- def get_frame(self):
71
- with self.lock:
72
- frame = self.circular_buffer[self.current_frame]
73
- if frame is not None:
74
- return frame
75
- else:
76
- return None
@@ -1,51 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: rocket-welder-sdk
3
- Version: 0.0.1
4
- Summary: Supporting sdk for RocketWelder
5
- Home-page: https://github.com/rocket-welder-sdk/rocket-welder-sdk
6
- Author: Rafal Maciag
7
- Author-email: rafal.maciag@modelingevolution.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.6
12
- Description-Content-Type: text/markdown
13
- Requires-Dist: numpy
14
- Requires-Dist: opencv-python
15
- Requires-Dist: requests
16
-
17
- # SDK for Rocket Welder
18
-
19
- Example
20
-
21
- ```python
22
- # main.py
23
- import cv2
24
- from rocket_welder_camera.camera import RocketWelderCamera
25
-
26
- def main():
27
- url = 'tcp://{YOUR-HOST}:{YOUR-PORT}/{STREAM-NAME}'
28
- cam = RocketWelderCamera(url)
29
-
30
- cv2.namedWindow('Frame', cv2.WINDOW_NORMAL)
31
-
32
- while True:
33
- frame = cam.get_frame()
34
- if frame is not None:
35
- # Display the frame
36
- cv2.imshow('Frame', frame)
37
-
38
- # Print the current frame number
39
- # print(f'Frame number: {cam.current_frame}')
40
-
41
- # Exit loop if 'q' is pressed
42
- if cv2.waitKey(1) & 0xFF == ord('q'):
43
- break
44
-
45
- cv2.destroyAllWindows()
46
-
47
- if __name__ == '__main__':
48
- main()
49
- ```
50
-
51
- Usually, YOUR-PORT is 7000 and YOUR-STREAM is "default".
@@ -1,8 +0,0 @@
1
- rocket_welder_camera/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- rocket_welder_camera/camera.py,sha256=IGyz6g6arKkc9-_JyK9GWa_iT5tE_t6qGk4EEw1kZcI,2629
3
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- tests/test_camera.py,sha256=8oHvTbQRhiCp8cYrEuTkLUpzpTROMd7m3paNLLAGg0c,1255
5
- rocket_welder_sdk-0.0.1.dist-info/METADATA,sha256=T2gcVRA0M91XbN7RzPpIc42-Fx69kJhYidvjE1mpFm0,1364
6
- rocket_welder_sdk-0.0.1.dist-info/WHEEL,sha256=rWxmBtp7hEUqVLOnTaDOPpR-cZpCDkzhhcBce-Zyd5k,91
7
- rocket_welder_sdk-0.0.1.dist-info/top_level.txt,sha256=qQCTu4qgoQUYLOGduATIONjktVBNsnWiYFNVB5_bEyY,27
8
- rocket_welder_sdk-0.0.1.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- rocket_welder_camera
2
- tests
tests/__init__.py DELETED
File without changes
tests/test_camera.py DELETED
@@ -1,36 +0,0 @@
1
- # tests/test_camera.py
2
- import unittest
3
- from unittest.mock import patch, MagicMock
4
- import numpy as np
5
- from rocket_welder_camera.camera import RocketWelderCamera
6
- import struct
7
-
8
- class TestRocketWelderCamera(unittest.TestCase):
9
- @patch('rocket_welder_camera.camera.socket.socket')
10
- @patch('rocket_welder_camera.camera.cv2.imdecode')
11
- def test_receive_frames(self, mock_imdecode, mock_socket):
12
- mock_sock_instance = MagicMock()
13
- mock_socket.return_value = mock_sock_instance
14
- mock_sock_instance.recv.side_effect = [
15
- struct.pack('>QQQ', 1, 5, 1), # Mock header
16
- b'\x00\x01\x02\x03\x04' # Mock frame data
17
- ]
18
-
19
- mock_imdecode.return_value = np.zeros((480, 640, 3), dtype=np.uint8)
20
-
21
- camera = RocketWelderCamera('tcp://pi-51:8082/a')
22
- camera.receive_frames()
23
-
24
-
25
- frame = camera.get_frame()
26
-
27
- self.assertIsNotNone(frame)
28
- self.assertEqual(frame.shape, (480, 640, 3))
29
-
30
- def test_get_frame_empty_buffer(self):
31
- camera = RocketWelderCamera('tcp://pi-51:8082/a')
32
- frame = camera.get_frame()
33
- self.assertIsNone(frame)
34
-
35
- if __name__ == '__main__':
36
- unittest.main()