matrice-streaming 0.1.70__py3-none-any.whl → 0.1.72__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/streaming_gateway/camera_streamer/async_camera_worker.py +36 -13
- matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py +12 -7
- {matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/METADATA +1 -1
- {matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/RECORD +7 -7
- {matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/WHEEL +0 -0
- {matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/top_level.txt +0 -0
|
@@ -100,7 +100,8 @@ class AsyncCameraWorker:
|
|
|
100
100
|
# PERFORMANCE: New parameters for optimized frame capture
|
|
101
101
|
# ================================================================
|
|
102
102
|
drop_stale_frames: bool = False, # Disabled for ML quality
|
|
103
|
-
|
|
103
|
+
use_simple_read: bool = True, # Use cap.read() directly instead of grab/retrieve
|
|
104
|
+
pin_cpu_affinity: bool = False, # Let OS distribute threads (avoids cache contention)
|
|
104
105
|
total_workers: int = 1, # Total worker count for CPU affinity calculation
|
|
105
106
|
buffer_size: int = 1, # Minimal buffer for low latency (cv2_bench uses 1)
|
|
106
107
|
):
|
|
@@ -120,6 +121,7 @@ class AsyncCameraWorker:
|
|
|
120
121
|
shm_slot_count: Number of frame slots per camera ring buffer
|
|
121
122
|
shm_frame_format: Frame format for SHM storage ("BGR", "RGB", or "NV12")
|
|
122
123
|
drop_stale_frames: Use grab()/grab()/retrieve() pattern to get latest frame
|
|
124
|
+
use_simple_read: Use cap.read() directly instead of grab/retrieve pattern
|
|
123
125
|
pin_cpu_affinity: Pin worker process to specific CPU cores for cache locality
|
|
124
126
|
total_workers: Total number of workers (for CPU affinity calculation)
|
|
125
127
|
buffer_size: VideoCapture buffer size (1 = minimal latency)
|
|
@@ -207,6 +209,7 @@ class AsyncCameraWorker:
|
|
|
207
209
|
# PERFORMANCE: Optimized frame capture configuration
|
|
208
210
|
# ================================================================
|
|
209
211
|
self.drop_stale_frames = drop_stale_frames
|
|
212
|
+
self.use_simple_read = use_simple_read
|
|
210
213
|
self.pin_cpu_affinity = pin_cpu_affinity
|
|
211
214
|
self.total_workers = total_workers
|
|
212
215
|
self.buffer_size = buffer_size
|
|
@@ -225,6 +228,9 @@ class AsyncCameraWorker:
|
|
|
225
228
|
if drop_stale_frames:
|
|
226
229
|
self.logger.info(f"Worker {worker_id}: Frame dropping ENABLED (grab/grab/retrieve pattern)")
|
|
227
230
|
|
|
231
|
+
if use_simple_read:
|
|
232
|
+
self.logger.info(f"Worker {worker_id}: Simple read ENABLED (cap.read() directly)")
|
|
233
|
+
|
|
228
234
|
# ThreadPoolExecutor for I/O-bound frame capture only
|
|
229
235
|
# Encoding is done inline (cv2.imencode releases GIL, ~5ms for 480p)
|
|
230
236
|
#
|
|
@@ -234,8 +240,8 @@ class AsyncCameraWorker:
|
|
|
234
240
|
# - But TOO many threads causes burst frame arrivals → Redis write queue backup
|
|
235
241
|
# - Cap at 64 threads to balance I/O parallelism vs write contention
|
|
236
242
|
num_cameras = len(camera_configs)
|
|
237
|
-
# Use 1 thread per camera,
|
|
238
|
-
num_capture_threads = min(64,
|
|
243
|
+
# Use 1 thread per camera, max 64 for reduced contention
|
|
244
|
+
num_capture_threads = min(64, num_cameras)
|
|
239
245
|
self.capture_executor = ThreadPoolExecutor(max_workers=num_capture_threads)
|
|
240
246
|
self.num_capture_threads = num_capture_threads
|
|
241
247
|
|
|
@@ -329,9 +335,10 @@ class AsyncCameraWorker:
|
|
|
329
335
|
|
|
330
336
|
# Create MatriceStream with async support
|
|
331
337
|
# Unpack stream_config as keyword arguments (MatriceStream expects **config)
|
|
338
|
+
# NOTE: enable_shm_batching=False for real-time frame delivery (avoids batch delays)
|
|
332
339
|
self.stream = MatriceStream(
|
|
333
340
|
stream_type=StreamType.REDIS,
|
|
334
|
-
enable_shm_batching=
|
|
341
|
+
enable_shm_batching=False,
|
|
335
342
|
**self.stream_config
|
|
336
343
|
)
|
|
337
344
|
|
|
@@ -409,23 +416,33 @@ class AsyncCameraWorker:
|
|
|
409
416
|
async def _read_latest_frame(
|
|
410
417
|
self,
|
|
411
418
|
cap: cv2.VideoCapture,
|
|
412
|
-
drop_stale: bool =
|
|
419
|
+
drop_stale: bool = False,
|
|
420
|
+
use_simple_read: bool = False,
|
|
413
421
|
) -> Tuple[bool, Optional[Any]]:
|
|
414
|
-
"""Read
|
|
422
|
+
"""Read frame from video capture.
|
|
415
423
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
424
|
+
Three modes available:
|
|
425
|
+
1. use_simple_read=True: Uses cap.read() directly - best for video files
|
|
426
|
+
2. drop_stale=True: Uses grab/grab/retrieve to skip stale buffered frames
|
|
427
|
+
3. drop_stale=False: Uses grab/retrieve (no frame dropping)
|
|
419
428
|
|
|
420
429
|
Args:
|
|
421
430
|
cap: OpenCV VideoCapture object
|
|
422
431
|
drop_stale: If True, use grab/grab/retrieve pattern to skip stale frames
|
|
432
|
+
use_simple_read: If True, use cap.read() directly (ignores drop_stale)
|
|
423
433
|
|
|
424
434
|
Returns:
|
|
425
435
|
Tuple of (success, frame) where frame is None if read failed
|
|
426
436
|
"""
|
|
427
437
|
loop = asyncio.get_event_loop()
|
|
428
438
|
|
|
439
|
+
# SIMPLE READ: Use cap.read() directly - best for video files
|
|
440
|
+
# This reads frames sequentially without any buffer manipulation
|
|
441
|
+
if use_simple_read:
|
|
442
|
+
ret, frame = await loop.run_in_executor(self.capture_executor, cap.read)
|
|
443
|
+
return ret, frame
|
|
444
|
+
|
|
445
|
+
# GRAB/RETRIEVE PATTERN: For live streams where we want latest frame
|
|
429
446
|
if drop_stale:
|
|
430
447
|
# Aggressive frame dropping: grab twice to get latest frame
|
|
431
448
|
# First grab clears any stale frame, second grab gets current
|
|
@@ -514,7 +531,9 @@ class AsyncCameraWorker:
|
|
|
514
531
|
# This gets the latest frame and drops stale buffered frames
|
|
515
532
|
read_start = time.time()
|
|
516
533
|
ret, frame = await self._read_latest_frame(
|
|
517
|
-
cap,
|
|
534
|
+
cap,
|
|
535
|
+
drop_stale=self.drop_stale_frames,
|
|
536
|
+
use_simple_read=self.use_simple_read,
|
|
518
537
|
)
|
|
519
538
|
read_time = time.time() - read_start
|
|
520
539
|
|
|
@@ -586,7 +605,8 @@ class AsyncCameraWorker:
|
|
|
586
605
|
return # Exit completely on cancellation
|
|
587
606
|
except Exception as exc:
|
|
588
607
|
self.logger.error(
|
|
589
|
-
f"Worker {self.worker_id}: Error in camera {stream_key}
|
|
608
|
+
f"Worker {self.worker_id}: Error in camera {stream_key} "
|
|
609
|
+
f"at frame {frame_counter}: {exc}",
|
|
590
610
|
exc_info=True
|
|
591
611
|
)
|
|
592
612
|
consecutive_failures += 1
|
|
@@ -596,7 +616,7 @@ class AsyncCameraWorker:
|
|
|
596
616
|
f"max failures in inner loop, reconnecting..."
|
|
597
617
|
)
|
|
598
618
|
break # Break inner loop to reconnect
|
|
599
|
-
await asyncio.sleep(1.
|
|
619
|
+
await asyncio.sleep(0.1) # Reduced from 1.0s to minimize frame loss
|
|
600
620
|
|
|
601
621
|
except asyncio.CancelledError:
|
|
602
622
|
self.logger.info(f"Worker {self.worker_id}: Camera {stream_key} task cancelled during setup")
|
|
@@ -1314,7 +1334,8 @@ def run_async_worker(
|
|
|
1314
1334
|
# PERFORMANCE: New parameters for optimized frame capture
|
|
1315
1335
|
# ================================================================
|
|
1316
1336
|
drop_stale_frames: bool = False, # Disabled for ML quality
|
|
1317
|
-
|
|
1337
|
+
use_simple_read: bool = True, # Use cap.read() directly instead of grab/retrieve
|
|
1338
|
+
pin_cpu_affinity: bool = False, # Let OS distribute (avoids cache contention)
|
|
1318
1339
|
total_workers: int = 1,
|
|
1319
1340
|
buffer_size: int = 1,
|
|
1320
1341
|
# ================================================================
|
|
@@ -1338,6 +1359,7 @@ def run_async_worker(
|
|
|
1338
1359
|
shm_slot_count: Number of frame slots per camera ring buffer
|
|
1339
1360
|
shm_frame_format: Frame format for SHM storage
|
|
1340
1361
|
drop_stale_frames: Use grab()/grab()/retrieve() pattern for latest frame
|
|
1362
|
+
use_simple_read: Use cap.read() directly instead of grab/retrieve pattern
|
|
1341
1363
|
pin_cpu_affinity: Pin worker process to specific CPU cores
|
|
1342
1364
|
total_workers: Total number of workers for CPU affinity calculation
|
|
1343
1365
|
buffer_size: VideoCapture buffer size (1 = minimal latency)
|
|
@@ -1380,6 +1402,7 @@ def run_async_worker(
|
|
|
1380
1402
|
shm_frame_format=shm_frame_format,
|
|
1381
1403
|
# PERFORMANCE: Pass through optimized frame capture parameters
|
|
1382
1404
|
drop_stale_frames=drop_stale_frames,
|
|
1405
|
+
use_simple_read=use_simple_read,
|
|
1383
1406
|
pin_cpu_affinity=pin_cpu_affinity,
|
|
1384
1407
|
total_workers=total_workers,
|
|
1385
1408
|
buffer_size=buffer_size,
|
|
@@ -44,7 +44,8 @@ class WorkerManager:
|
|
|
44
44
|
# PERFORMANCE: New parameters for optimized frame capture
|
|
45
45
|
# ================================================================
|
|
46
46
|
drop_stale_frames: bool = False, # Use grab()/grab()/retrieve() for latest frame (disabled for ML quality)
|
|
47
|
-
|
|
47
|
+
use_simple_read: bool = True, # Use cap.read() directly instead of grab/retrieve
|
|
48
|
+
pin_cpu_affinity: bool = False, # Let OS distribute (avoids cache contention)
|
|
48
49
|
buffer_size: int = 1, # VideoCapture buffer size (1 = minimal latency)
|
|
49
50
|
# ================================================================
|
|
50
51
|
# FRAME OPTIMIZER: Control frame similarity detection
|
|
@@ -64,6 +65,7 @@ class WorkerManager:
|
|
|
64
65
|
shm_slot_count: Number of frame slots per camera ring buffer
|
|
65
66
|
shm_frame_format: Frame format for SHM storage
|
|
66
67
|
drop_stale_frames: Use grab()/grab()/retrieve() pattern for latest frame
|
|
68
|
+
use_simple_read: Use cap.read() directly instead of grab/retrieve pattern
|
|
67
69
|
pin_cpu_affinity: Pin worker processes to specific CPU cores for cache locality
|
|
68
70
|
buffer_size: VideoCapture buffer size (1 = minimal latency)
|
|
69
71
|
frame_optimizer_enabled: Enable frame similarity detection (skip similar frames for bandwidth saving)
|
|
@@ -76,17 +78,18 @@ class WorkerManager:
|
|
|
76
78
|
cpu_count = os.cpu_count() or 4 # Fallback to 4 if can't detect
|
|
77
79
|
num_cameras = len(camera_configs)
|
|
78
80
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
# Small deployments: 1 worker per camera for maximum parallelism
|
|
82
|
+
# Each worker process has its own GIL, enabling true CPU parallelism
|
|
83
|
+
if num_cameras <= 10:
|
|
84
|
+
calculated_workers = max(1, num_cameras)
|
|
85
|
+
# Large deployments: use camera-based calculation
|
|
86
|
+
elif cpu_count >= 16 or num_cameras >= 100:
|
|
84
87
|
# Use camera-based calculation for better distribution
|
|
85
88
|
# 1000 cameras / 25 cameras per worker = 40 workers
|
|
86
89
|
target_cameras_per_worker = 25
|
|
87
90
|
calculated_workers = max(4, min(num_cameras // target_cameras_per_worker, 50))
|
|
88
91
|
else:
|
|
89
|
-
# Standard calculation for
|
|
92
|
+
# Standard calculation for medium systems
|
|
90
93
|
calculated_workers = max(4, int(cpu_count * cpu_percentage))
|
|
91
94
|
|
|
92
95
|
# Cap at camera count (no point having more workers than cameras)
|
|
@@ -124,6 +127,7 @@ class WorkerManager:
|
|
|
124
127
|
# PERFORMANCE: Store optimized frame capture configuration
|
|
125
128
|
# ================================================================
|
|
126
129
|
self.drop_stale_frames = drop_stale_frames
|
|
130
|
+
self.use_simple_read = use_simple_read
|
|
127
131
|
self.pin_cpu_affinity = pin_cpu_affinity
|
|
128
132
|
self.buffer_size = buffer_size
|
|
129
133
|
|
|
@@ -307,6 +311,7 @@ class WorkerManager:
|
|
|
307
311
|
self.shm_frame_format,
|
|
308
312
|
# PERFORMANCE: Pass optimized frame capture parameters
|
|
309
313
|
self.drop_stale_frames,
|
|
314
|
+
self.use_simple_read,
|
|
310
315
|
self.pin_cpu_affinity,
|
|
311
316
|
self.num_workers, # Total workers for CPU affinity calculation
|
|
312
317
|
self.buffer_size,
|
|
@@ -19,7 +19,7 @@ matrice_streaming/streaming_gateway/streaming_gateway_utils.py,sha256=6kNYgl3f7H
|
|
|
19
19
|
matrice_streaming/streaming_gateway/streaming_status_listener.py,sha256=RgbW0xYbbpmO6ZjkVlh6fg8iqkpRaIShR2dQ3SMVFUw,3161
|
|
20
20
|
matrice_streaming/streaming_gateway/camera_streamer/ARCHITECTURE.md,sha256=kngsqiS1PdNYhihBoMtoiIf3THJ4OM33E_hxExqzKqY,9980
|
|
21
21
|
matrice_streaming/streaming_gateway/camera_streamer/__init__.py,sha256=nwVY-ySnKvOedeDXakyR_6KPHSz3yzSeaO_4IFMMP4I,2219
|
|
22
|
-
matrice_streaming/streaming_gateway/camera_streamer/async_camera_worker.py,sha256=
|
|
22
|
+
matrice_streaming/streaming_gateway/camera_streamer/async_camera_worker.py,sha256=W6hEeh0KLGXIBAixDIomAAOOoo5QectCp1__RHLS-ko,61242
|
|
23
23
|
matrice_streaming/streaming_gateway/camera_streamer/async_ffmpeg_worker.py,sha256=cD3XocWqamkBE9TlkG757OK6tl_Op45r-cMd-ZgJXaA,37063
|
|
24
24
|
matrice_streaming/streaming_gateway/camera_streamer/camera_streamer.py,sha256=zv6tWUKQf2uXAxqtSlfwAVDb9N483VCRApPogUWJ9e8,39113
|
|
25
25
|
matrice_streaming/streaming_gateway/camera_streamer/device_detection.py,sha256=9F4rsbMpIexOIlX8aCj7Q6PFG01kOS1wtgAIQBG0FaM,18463
|
|
@@ -39,7 +39,7 @@ matrice_streaming/streaming_gateway/camera_streamer/platform_pipelines.py,sha256
|
|
|
39
39
|
matrice_streaming/streaming_gateway/camera_streamer/retry_manager.py,sha256=d8tlGoWoeSlgpCgXbUHTM61ekCQZki7TO1HzL2yPVzk,3607
|
|
40
40
|
matrice_streaming/streaming_gateway/camera_streamer/stream_statistics.py,sha256=VC1S6ogiHUTlzTqn2wGOC1ClOEjvDgWm9Ydi9sWj-Do,18951
|
|
41
41
|
matrice_streaming/streaming_gateway/camera_streamer/video_capture_manager.py,sha256=ySnbihdkpFPcBdFK_OFcHE-MgjG2YirUUYgEJHhvTxo,16899
|
|
42
|
-
matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py,sha256=
|
|
42
|
+
matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py,sha256=Vfdz9DaLkkTlRqg2_DKk4pkUmxQbEaI44c82X7ro8OU,29322
|
|
43
43
|
matrice_streaming/streaming_gateway/debug/README.md,sha256=6GeHClMjJbmVuSKbJ8yOIDqpeAPnLNrHXMFtmubZz0E,11343
|
|
44
44
|
matrice_streaming/streaming_gateway/debug/__init__.py,sha256=_RtvH0Y12HFiDoKK8BEsMwy6k_iAFjmYYNzMawbOfeQ,2026
|
|
45
45
|
matrice_streaming/streaming_gateway/debug/benchmark.py,sha256=65la9K2mjiiuADCBSnYpPr9uYFnM89clgw-KHkjSkAU,25563
|
|
@@ -49,8 +49,8 @@ matrice_streaming/streaming_gateway/debug/debug_streaming_gateway.py,sha256=ZiDg
|
|
|
49
49
|
matrice_streaming/streaming_gateway/debug/debug_utils.py,sha256=jWcSBgrk_YVt1QzSyw6geX17YBnTvgVdA5ubqO531a0,10477
|
|
50
50
|
matrice_streaming/streaming_gateway/debug/example_debug_streaming.py,sha256=-gS8zNDswAoj6oss66QQWYZhY24usfLiMH0FFK06vV0,7994
|
|
51
51
|
matrice_streaming/streaming_gateway/debug/test_videoplayback.py,sha256=s_dgWkoESiuJHlUAf_iv4d7OGmAhwocwDZmIcFUZzvo,11093
|
|
52
|
-
matrice_streaming-0.1.
|
|
53
|
-
matrice_streaming-0.1.
|
|
54
|
-
matrice_streaming-0.1.
|
|
55
|
-
matrice_streaming-0.1.
|
|
56
|
-
matrice_streaming-0.1.
|
|
52
|
+
matrice_streaming-0.1.72.dist-info/licenses/LICENSE.txt,sha256=_uQUZpgO0mRYL5-fPoEvLSbNnLPv6OmbeEDCHXhK6Qc,1066
|
|
53
|
+
matrice_streaming-0.1.72.dist-info/METADATA,sha256=R5X_pdLFu9o3SUw0LXwB0xsbxvk0b3We7IbCpoRwgQc,2477
|
|
54
|
+
matrice_streaming-0.1.72.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
55
|
+
matrice_streaming-0.1.72.dist-info/top_level.txt,sha256=PM_trIe8f4JLc90J871rNMYGVM3Po9Inx4As5LrCFUU,18
|
|
56
|
+
matrice_streaming-0.1.72.dist-info/RECORD,,
|
|
File without changes
|
{matrice_streaming-0.1.70.dist-info → matrice_streaming-0.1.72.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|