matrice-streaming 0.1.14__py3-none-any.whl → 0.1.65__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.
- matrice_streaming/__init__.py +44 -32
- matrice_streaming/streaming_gateway/camera_streamer/__init__.py +68 -1
- matrice_streaming/streaming_gateway/camera_streamer/async_camera_worker.py +1388 -0
- matrice_streaming/streaming_gateway/camera_streamer/async_ffmpeg_worker.py +966 -0
- matrice_streaming/streaming_gateway/camera_streamer/camera_streamer.py +188 -24
- matrice_streaming/streaming_gateway/camera_streamer/device_detection.py +507 -0
- matrice_streaming/streaming_gateway/camera_streamer/encoding_pool_manager.py +136 -0
- matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_camera_streamer.py +1048 -0
- matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_config.py +192 -0
- matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_worker_manager.py +470 -0
- matrice_streaming/streaming_gateway/camera_streamer/gstreamer_camera_streamer.py +1368 -0
- matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker.py +1063 -0
- matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker_manager.py +546 -0
- matrice_streaming/streaming_gateway/camera_streamer/message_builder.py +60 -15
- matrice_streaming/streaming_gateway/camera_streamer/nvdec.py +1330 -0
- matrice_streaming/streaming_gateway/camera_streamer/nvdec_worker_manager.py +412 -0
- matrice_streaming/streaming_gateway/camera_streamer/platform_pipelines.py +680 -0
- matrice_streaming/streaming_gateway/camera_streamer/stream_statistics.py +111 -4
- matrice_streaming/streaming_gateway/camera_streamer/video_capture_manager.py +223 -27
- matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py +694 -0
- matrice_streaming/streaming_gateway/debug/__init__.py +27 -2
- matrice_streaming/streaming_gateway/debug/benchmark.py +727 -0
- matrice_streaming/streaming_gateway/debug/debug_gstreamer_gateway.py +599 -0
- matrice_streaming/streaming_gateway/debug/debug_streaming_gateway.py +245 -95
- matrice_streaming/streaming_gateway/debug/debug_utils.py +29 -0
- matrice_streaming/streaming_gateway/debug/test_videoplayback.py +318 -0
- matrice_streaming/streaming_gateway/dynamic_camera_manager.py +656 -39
- matrice_streaming/streaming_gateway/metrics_reporter.py +676 -139
- matrice_streaming/streaming_gateway/streaming_action.py +71 -20
- matrice_streaming/streaming_gateway/streaming_gateway.py +1026 -78
- matrice_streaming/streaming_gateway/streaming_gateway_utils.py +175 -20
- matrice_streaming/streaming_gateway/streaming_status_listener.py +89 -0
- {matrice_streaming-0.1.14.dist-info → matrice_streaming-0.1.65.dist-info}/METADATA +1 -1
- matrice_streaming-0.1.65.dist-info/RECORD +56 -0
- matrice_streaming-0.1.14.dist-info/RECORD +0 -38
- {matrice_streaming-0.1.14.dist-info → matrice_streaming-0.1.65.dist-info}/WHEEL +0 -0
- {matrice_streaming-0.1.14.dist-info → matrice_streaming-0.1.65.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_streaming-0.1.14.dist-info → matrice_streaming-0.1.65.dist-info}/top_level.txt +0 -0
|
@@ -5,8 +5,9 @@ from typing import List, Optional, Dict, Any
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from .debug_stream_backend import DebugStreamBackend
|
|
8
|
-
from .debug_utils import MockSession, create_debug_input_streams
|
|
8
|
+
from .debug_utils import MockSession, create_debug_input_streams, create_camera_configs_from_streams
|
|
9
9
|
from ..camera_streamer.camera_streamer import CameraStreamer
|
|
10
|
+
from ..camera_streamer.worker_manager import WorkerManager
|
|
10
11
|
|
|
11
12
|
class DebugStreamingGateway:
|
|
12
13
|
"""Debug version of StreamingGateway that works without external dependencies.
|
|
@@ -55,9 +56,14 @@ class DebugStreamingGateway:
|
|
|
55
56
|
save_frame_data: bool = False,
|
|
56
57
|
width: Optional[int] = None,
|
|
57
58
|
height: Optional[int] = None,
|
|
59
|
+
# Worker mode parameters (Mode 2)
|
|
60
|
+
use_workers: bool = False,
|
|
61
|
+
num_workers: Optional[int] = None,
|
|
62
|
+
cpu_percentage: float = 0.8,
|
|
63
|
+
max_cameras_per_worker: int = 10,
|
|
58
64
|
):
|
|
59
65
|
"""Initialize debug streaming gateway.
|
|
60
|
-
|
|
66
|
+
|
|
61
67
|
Args:
|
|
62
68
|
video_paths: List of video file paths to stream
|
|
63
69
|
fps: Frames per second to stream
|
|
@@ -71,20 +77,25 @@ class DebugStreamingGateway:
|
|
|
71
77
|
save_frame_data: Include frame data in saved files
|
|
72
78
|
width: Override video width
|
|
73
79
|
height: Override video height
|
|
80
|
+
use_workers: Use multi-process WorkerManager (Mode 2) instead of single-threaded CameraStreamer
|
|
81
|
+
num_workers: Number of worker processes (auto-calculated if None)
|
|
82
|
+
cpu_percentage: Percentage of CPU cores to use for auto-calculation
|
|
83
|
+
max_cameras_per_worker: Maximum cameras per worker process
|
|
74
84
|
"""
|
|
75
85
|
# Validate video paths
|
|
76
86
|
for video_path in video_paths:
|
|
77
87
|
if not Path(video_path).exists():
|
|
78
88
|
raise FileNotFoundError(f"Video file not found: {video_path}")
|
|
79
|
-
|
|
89
|
+
|
|
80
90
|
self.video_paths = video_paths
|
|
81
91
|
self.fps = fps
|
|
82
92
|
self.video_codec = video_codec
|
|
83
93
|
self.loop_videos = loop_videos
|
|
84
|
-
|
|
94
|
+
self.use_workers = use_workers
|
|
95
|
+
|
|
85
96
|
# Create mock session
|
|
86
97
|
self.session = MockSession()
|
|
87
|
-
|
|
98
|
+
|
|
88
99
|
# Create debug stream backend (replaces Kafka/Redis)
|
|
89
100
|
self.stream_backend = DebugStreamBackend(
|
|
90
101
|
output_dir=output_dir,
|
|
@@ -92,14 +103,14 @@ class DebugStreamingGateway:
|
|
|
92
103
|
log_messages=log_messages,
|
|
93
104
|
save_frame_data=save_frame_data
|
|
94
105
|
)
|
|
95
|
-
|
|
106
|
+
|
|
96
107
|
# Create input streams from video paths
|
|
97
108
|
self.input_streams = create_debug_input_streams(
|
|
98
109
|
video_paths=video_paths,
|
|
99
110
|
fps=fps,
|
|
100
111
|
loop=loop_videos
|
|
101
112
|
)
|
|
102
|
-
|
|
113
|
+
|
|
103
114
|
# Override dimensions if provided
|
|
104
115
|
if width or height:
|
|
105
116
|
for stream in self.input_streams:
|
|
@@ -107,82 +118,141 @@ class DebugStreamingGateway:
|
|
|
107
118
|
stream.width = width
|
|
108
119
|
if height:
|
|
109
120
|
stream.height = height
|
|
110
|
-
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
|
|
122
|
+
# Determine h265_mode from video_codec
|
|
123
|
+
# h265_mode can be "frame" or "stream" (chunk is part of stream mode)
|
|
124
|
+
h265_mode = "frame"
|
|
125
|
+
if "chunk" in video_codec.lower() or "stream" in video_codec.lower():
|
|
126
|
+
h265_mode = "stream"
|
|
127
|
+
|
|
128
|
+
# Initialize camera streamer or worker manager based on mode
|
|
129
|
+
self.camera_streamer = None
|
|
130
|
+
self.worker_manager = None
|
|
131
|
+
|
|
132
|
+
if use_workers:
|
|
133
|
+
# Mode 2: WorkerManager (multi-process async workers)
|
|
134
|
+
# Create camera configs from input streams
|
|
135
|
+
camera_configs = create_camera_configs_from_streams(self.input_streams)
|
|
136
|
+
|
|
137
|
+
# Create stream config for workers
|
|
138
|
+
stream_config = {
|
|
139
|
+
'service_id': 'debug_streaming_gateway',
|
|
140
|
+
'server_type': 'debug',
|
|
141
|
+
'video_codec': video_codec,
|
|
142
|
+
'h265_quality': h265_quality,
|
|
143
|
+
'use_hardware': use_hardware,
|
|
144
|
+
'h265_mode': h265_mode,
|
|
145
|
+
# Debug backend will be injected into workers
|
|
146
|
+
'debug_mode': True,
|
|
147
|
+
'debug_backend_config': {
|
|
148
|
+
'output_dir': output_dir,
|
|
149
|
+
'save_to_files': save_to_files,
|
|
150
|
+
'log_messages': log_messages,
|
|
151
|
+
'save_frame_data': save_frame_data,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
self.worker_manager = WorkerManager(
|
|
156
|
+
camera_configs=camera_configs,
|
|
157
|
+
stream_config=stream_config,
|
|
158
|
+
num_workers=num_workers,
|
|
159
|
+
cpu_percentage=cpu_percentage,
|
|
160
|
+
max_cameras_per_worker=max_cameras_per_worker,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.logger = logging.getLogger(__name__)
|
|
164
|
+
self.logger.info(
|
|
165
|
+
f"DebugStreamingGateway initialized (Worker Mode): "
|
|
166
|
+
f"{len(video_paths)} videos, "
|
|
167
|
+
f"{fps} fps, "
|
|
168
|
+
f"codec={video_codec}, "
|
|
169
|
+
f"workers={self.worker_manager.num_workers}"
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
# Mode 1: CameraStreamer (single-threaded)
|
|
173
|
+
self.camera_streamer = CameraStreamer(
|
|
174
|
+
session=self.session,
|
|
175
|
+
service_id="debug_streaming_gateway",
|
|
176
|
+
server_type="debug",
|
|
177
|
+
video_codec=video_codec,
|
|
178
|
+
h265_quality=h265_quality,
|
|
179
|
+
use_hardware=use_hardware,
|
|
180
|
+
h265_mode=h265_mode,
|
|
181
|
+
gateway_util=None, # No gateway_util in debug mode
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Replace MatriceStream with debug backend
|
|
185
|
+
self.camera_streamer.matrice_stream = self.stream_backend
|
|
186
|
+
|
|
187
|
+
self.logger = logging.getLogger(__name__)
|
|
188
|
+
self.logger.info(
|
|
189
|
+
f"DebugStreamingGateway initialized (Single-threaded Mode): "
|
|
190
|
+
f"{len(video_paths)} videos, "
|
|
191
|
+
f"{fps} fps, "
|
|
192
|
+
f"codec={video_codec}"
|
|
193
|
+
)
|
|
194
|
+
|
|
125
195
|
# State
|
|
126
196
|
self.is_streaming = False
|
|
127
197
|
self.start_time = None
|
|
128
|
-
|
|
129
|
-
self.logger = logging.getLogger(__name__)
|
|
130
|
-
self.logger.info(
|
|
131
|
-
f"DebugStreamingGateway initialized: "
|
|
132
|
-
f"{len(video_paths)} videos, "
|
|
133
|
-
f"{fps} fps, "
|
|
134
|
-
f"codec={video_codec}"
|
|
135
|
-
)
|
|
136
198
|
|
|
137
199
|
def start_streaming(self, block: bool = False) -> bool:
|
|
138
200
|
"""Start streaming all video files.
|
|
139
|
-
|
|
201
|
+
|
|
140
202
|
Args:
|
|
141
203
|
block: If True, block until manually stopped
|
|
142
|
-
|
|
204
|
+
|
|
143
205
|
Returns:
|
|
144
206
|
True if started successfully
|
|
145
207
|
"""
|
|
146
208
|
if self.is_streaming:
|
|
147
209
|
self.logger.warning("Already streaming")
|
|
148
210
|
return False
|
|
149
|
-
|
|
211
|
+
|
|
150
212
|
self.logger.info(f"Starting debug streaming with {len(self.input_streams)} videos")
|
|
151
|
-
|
|
213
|
+
|
|
152
214
|
try:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
215
|
+
if self.use_workers:
|
|
216
|
+
# Mode 2: Worker manager mode
|
|
217
|
+
self.worker_manager.start()
|
|
218
|
+
self.is_streaming = True
|
|
219
|
+
self.start_time = time.time()
|
|
220
|
+
self.logger.info(f"Worker manager started with {self.worker_manager.num_workers} workers")
|
|
221
|
+
else:
|
|
222
|
+
# Mode 1: Single-threaded camera streamer mode
|
|
223
|
+
# Register topics and start streams
|
|
224
|
+
for i, input_stream in enumerate(self.input_streams):
|
|
225
|
+
stream_key = input_stream.camera_key
|
|
226
|
+
topic = input_stream.camera_input_topic
|
|
227
|
+
|
|
228
|
+
# Register topic
|
|
229
|
+
self.camera_streamer.register_stream_topic(stream_key, topic)
|
|
230
|
+
self.stream_backend.setup(topic)
|
|
231
|
+
|
|
232
|
+
# Start streaming
|
|
233
|
+
success = self.camera_streamer.start_background_stream(
|
|
234
|
+
input=input_stream.source,
|
|
235
|
+
fps=input_stream.fps,
|
|
236
|
+
stream_key=stream_key,
|
|
237
|
+
stream_group_key=input_stream.camera_group_key,
|
|
238
|
+
quality=input_stream.quality,
|
|
239
|
+
width=input_stream.width,
|
|
240
|
+
height=input_stream.height,
|
|
241
|
+
simulate_video_file_stream=input_stream.simulate_video_file_stream,
|
|
242
|
+
camera_location=input_stream.camera_location,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if not success:
|
|
246
|
+
self.logger.error(f"Failed to start stream {i}: {input_stream.source}")
|
|
247
|
+
self.stop_streaming()
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
self.logger.info(f"Started stream {i}: {input_stream.source}")
|
|
251
|
+
|
|
252
|
+
self.is_streaming = True
|
|
253
|
+
self.start_time = time.time()
|
|
254
|
+
self.logger.info("Debug streaming started successfully")
|
|
255
|
+
|
|
186
256
|
if block:
|
|
187
257
|
self.logger.info("Blocking mode - press Ctrl+C to stop")
|
|
188
258
|
try:
|
|
@@ -191,9 +261,9 @@ class DebugStreamingGateway:
|
|
|
191
261
|
except KeyboardInterrupt:
|
|
192
262
|
self.logger.info("Interrupted by user")
|
|
193
263
|
self.stop_streaming()
|
|
194
|
-
|
|
264
|
+
|
|
195
265
|
return True
|
|
196
|
-
|
|
266
|
+
|
|
197
267
|
except Exception as e:
|
|
198
268
|
self.logger.error(f"Failed to start debug streaming: {e}", exc_info=True)
|
|
199
269
|
self.stop_streaming()
|
|
@@ -204,23 +274,29 @@ class DebugStreamingGateway:
|
|
|
204
274
|
if not self.is_streaming:
|
|
205
275
|
self.logger.warning("Not streaming")
|
|
206
276
|
return
|
|
207
|
-
|
|
277
|
+
|
|
208
278
|
self.logger.info("Stopping debug streaming")
|
|
209
|
-
|
|
279
|
+
|
|
210
280
|
try:
|
|
211
|
-
self.
|
|
212
|
-
|
|
281
|
+
if self.use_workers:
|
|
282
|
+
# Mode 2: Stop worker manager
|
|
283
|
+
self.worker_manager.stop()
|
|
284
|
+
else:
|
|
285
|
+
# Mode 1: Stop camera streamer
|
|
286
|
+
self.camera_streamer.stop_streaming()
|
|
287
|
+
self.stream_backend.close()
|
|
288
|
+
|
|
213
289
|
self.is_streaming = False
|
|
214
|
-
|
|
290
|
+
|
|
215
291
|
runtime = time.time() - self.start_time if self.start_time else 0
|
|
216
292
|
self.logger.info(f"Debug streaming stopped (runtime: {runtime:.1f}s)")
|
|
217
|
-
|
|
293
|
+
|
|
218
294
|
except Exception as e:
|
|
219
295
|
self.logger.error(f"Error stopping debug streaming: {e}", exc_info=True)
|
|
220
296
|
|
|
221
297
|
def get_statistics(self) -> Dict[str, Any]:
|
|
222
298
|
"""Get streaming statistics.
|
|
223
|
-
|
|
299
|
+
|
|
224
300
|
Returns:
|
|
225
301
|
Dictionary with streaming statistics
|
|
226
302
|
"""
|
|
@@ -229,40 +305,112 @@ class DebugStreamingGateway:
|
|
|
229
305
|
"video_count": len(self.video_paths),
|
|
230
306
|
"fps": self.fps,
|
|
231
307
|
"video_codec": self.video_codec,
|
|
308
|
+
"mode": "worker" if self.use_workers else "single-threaded",
|
|
232
309
|
}
|
|
233
|
-
|
|
310
|
+
|
|
234
311
|
if self.start_time:
|
|
235
312
|
stats["runtime_seconds"] = time.time() - self.start_time
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
313
|
+
|
|
314
|
+
if self.use_workers:
|
|
315
|
+
# Mode 2: Get worker manager stats
|
|
316
|
+
try:
|
|
317
|
+
# Get worker statistics
|
|
318
|
+
worker_stats = self.worker_manager.get_worker_statistics()
|
|
319
|
+
stats["worker_stats"] = worker_stats
|
|
320
|
+
|
|
321
|
+
# Calculate aggregate transmission stats from health_reports
|
|
322
|
+
total_frames = 0
|
|
323
|
+
total_bytes = 0
|
|
324
|
+
active_streams = 0
|
|
325
|
+
total_errors = 0
|
|
326
|
+
|
|
327
|
+
# The worker_stats has a 'health_reports' dict with per-worker metrics
|
|
328
|
+
health_reports = worker_stats.get('health_reports', {})
|
|
329
|
+
for worker_id, report in health_reports.items():
|
|
330
|
+
if isinstance(report, dict):
|
|
331
|
+
metrics = report.get('metrics', {})
|
|
332
|
+
# Workers use frames_encoded for frame count
|
|
333
|
+
total_frames += metrics.get('frames_encoded', 0)
|
|
334
|
+
total_bytes += metrics.get('bytes_sent', 0)
|
|
335
|
+
active_streams += report.get('active_cameras', 0)
|
|
336
|
+
total_errors += metrics.get('encoding_errors', 0)
|
|
337
|
+
|
|
338
|
+
stats["transmission_stats"] = {
|
|
339
|
+
"total_frames_sent": total_frames,
|
|
340
|
+
"total_bytes_sent": total_bytes,
|
|
341
|
+
"active_streams": active_streams,
|
|
342
|
+
"num_workers": worker_stats.get('num_workers', self.worker_manager.num_workers),
|
|
343
|
+
"running_workers": worker_stats.get('running_workers', 0),
|
|
344
|
+
"total_errors": total_errors,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# Calculate derived metrics
|
|
348
|
+
if self.start_time:
|
|
349
|
+
duration = time.time() - self.start_time
|
|
350
|
+
stats["avg_fps"] = total_frames / duration if duration > 0 else 0
|
|
351
|
+
stats["bandwidth_mbps"] = (total_bytes * 8) / (duration * 1_000_000) if duration > 0 else 0
|
|
352
|
+
|
|
353
|
+
except Exception as e:
|
|
354
|
+
self.logger.warning(f"Failed to get worker stats: {e}")
|
|
355
|
+
stats["transmission_stats"] = {}
|
|
356
|
+
else:
|
|
357
|
+
# Mode 1: Get camera streamer stats
|
|
358
|
+
try:
|
|
359
|
+
transmission_stats = self.camera_streamer.get_transmission_stats()
|
|
360
|
+
|
|
361
|
+
# Normalize field names for consistency
|
|
362
|
+
# CameraStreamer uses frames_sent_full, total_frames_processed
|
|
363
|
+
total_frames = transmission_stats.get("total_frames_processed", 0)
|
|
364
|
+
total_bytes = transmission_stats.get("total_bytes_sent", 0)
|
|
365
|
+
|
|
366
|
+
# Get backend stats for additional metrics
|
|
367
|
+
backend_stats = self.stream_backend.get_statistics()
|
|
368
|
+
if not total_frames and backend_stats:
|
|
369
|
+
total_frames = backend_stats.get("total_messages", 0)
|
|
370
|
+
|
|
371
|
+
# Update transmission_stats with normalized field names
|
|
372
|
+
transmission_stats["total_frames_sent"] = total_frames
|
|
373
|
+
stats["transmission_stats"] = transmission_stats
|
|
374
|
+
|
|
375
|
+
# Calculate derived metrics
|
|
376
|
+
if self.start_time:
|
|
377
|
+
duration = time.time() - self.start_time
|
|
378
|
+
stats["avg_fps"] = total_frames / duration if duration > 0 else 0
|
|
379
|
+
stats["bandwidth_mbps"] = (total_bytes * 8) / (duration * 1_000_000) if duration > 0 else 0
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
self.logger.warning(f"Failed to get transmission stats: {e}")
|
|
383
|
+
|
|
384
|
+
# Get backend stats
|
|
385
|
+
try:
|
|
386
|
+
stats["backend_stats"] = self.stream_backend.get_statistics()
|
|
387
|
+
except Exception as e:
|
|
388
|
+
self.logger.warning(f"Failed to get backend stats: {e}")
|
|
389
|
+
|
|
249
390
|
return stats
|
|
250
391
|
|
|
251
392
|
def get_timing_stats(self, stream_key: Optional[str] = None) -> Dict[str, Any]:
|
|
252
393
|
"""Get timing statistics.
|
|
253
|
-
|
|
394
|
+
|
|
254
395
|
Args:
|
|
255
396
|
stream_key: Specific stream or None for all
|
|
256
|
-
|
|
397
|
+
|
|
257
398
|
Returns:
|
|
258
399
|
Timing statistics
|
|
259
400
|
"""
|
|
401
|
+
if self.use_workers:
|
|
402
|
+
# Worker mode doesn't support per-stream timing stats directly
|
|
403
|
+
return {"mode": "worker", "note": "Per-stream timing not available in worker mode"}
|
|
260
404
|
return self.camera_streamer.get_stream_timing_stats(stream_key)
|
|
261
|
-
|
|
405
|
+
|
|
262
406
|
def reset_stats(self):
|
|
263
407
|
"""Reset all statistics."""
|
|
264
|
-
self.
|
|
265
|
-
|
|
408
|
+
if self.use_workers:
|
|
409
|
+
# Worker mode - no direct reset capability
|
|
410
|
+
self.logger.info("Statistics reset (worker mode - limited reset)")
|
|
411
|
+
else:
|
|
412
|
+
self.camera_streamer.reset_transmission_stats()
|
|
413
|
+
self.logger.info("Statistics reset")
|
|
266
414
|
|
|
267
415
|
def __enter__(self):
|
|
268
416
|
"""Context manager entry."""
|
|
@@ -276,10 +424,12 @@ class DebugStreamingGateway:
|
|
|
276
424
|
|
|
277
425
|
def __repr__(self):
|
|
278
426
|
"""String representation."""
|
|
427
|
+
mode_str = f"workers={self.worker_manager.num_workers}" if self.use_workers else "single-threaded"
|
|
279
428
|
return (
|
|
280
429
|
f"DebugStreamingGateway("
|
|
281
430
|
f"videos={len(self.video_paths)}, "
|
|
282
431
|
f"fps={self.fps}, "
|
|
432
|
+
f"mode={mode_str}, "
|
|
283
433
|
f"streaming={self.is_streaming})"
|
|
284
434
|
)
|
|
285
435
|
|
|
@@ -278,3 +278,32 @@ def create_debug_input_streams(video_paths: List[str], fps: int = 10, loop: bool
|
|
|
278
278
|
|
|
279
279
|
return input_streams
|
|
280
280
|
|
|
281
|
+
|
|
282
|
+
def create_camera_configs_from_streams(input_streams: List) -> List[Dict[str, Any]]:
|
|
283
|
+
"""Convert InputStream objects to camera configs for WorkerManager.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
input_streams: List of InputStream objects
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
List of camera configuration dictionaries for WorkerManager
|
|
290
|
+
"""
|
|
291
|
+
camera_configs = []
|
|
292
|
+
|
|
293
|
+
for stream in input_streams:
|
|
294
|
+
config = {
|
|
295
|
+
'source': stream.source,
|
|
296
|
+
'stream_key': stream.camera_key,
|
|
297
|
+
'stream_group_key': stream.camera_group_key,
|
|
298
|
+
'topic': stream.camera_input_topic,
|
|
299
|
+
'fps': stream.fps,
|
|
300
|
+
'quality': stream.quality,
|
|
301
|
+
'width': stream.width,
|
|
302
|
+
'height': stream.height,
|
|
303
|
+
'simulate_video_file_stream': stream.simulate_video_file_stream,
|
|
304
|
+
'camera_location': stream.camera_location,
|
|
305
|
+
}
|
|
306
|
+
camera_configs.append(config)
|
|
307
|
+
|
|
308
|
+
return camera_configs
|
|
309
|
+
|