matrice-streaming 0.1.73__tar.gz → 0.1.74__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.
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/PKG-INFO +1 -1
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/PKG-INFO +1 -1
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/nvdec.py +65 -9
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/LICENSE.txt +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/README.md +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/SOURCES.txt +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/dependency_links.txt +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/not-zip-safe +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/top_level.txt +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/pyproject.toml +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/setup.cfg +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/setup.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/client.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/client_utils.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/camera_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/deployment.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/inference_pipeline.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/streaming_gateway_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/todo.txt +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/py.typed +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/ARCHITECTURE.md +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/async_camera_worker.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/async_ffmpeg_worker.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/camera_streamer.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/device_detection.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/encoder_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/encoding_pool_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_camera_streamer.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_config.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/ffmpeg_worker_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/frame_processor.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_camera_streamer.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/gstreamer_worker_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/message_builder.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/nvdec_worker_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/platform_pipelines.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/retry_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/stream_statistics.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/video_capture_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/camera_streamer/worker_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/README.md +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/__init__.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/benchmark.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/debug_gstreamer_gateway.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/debug_stream_backend.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/debug_streaming_gateway.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/debug_utils.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/example_debug_streaming.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/debug/test_videoplayback.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/dynamic_camera_manager.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/event_listener.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/metrics_reporter.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/streaming_action.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/streaming_gateway.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/streaming_gateway_utils.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/streaming_gateway/streaming_status_listener.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_async_infrastructure.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_batch_auto_calculation.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_batching_verification.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_e2e_production.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_flatten_binary.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_gstreamer_integration.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_msgpack_fix.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_phase1_unit.py +0 -0
- {matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/tests/test_phase2_scaling.py +0 -0
|
@@ -393,6 +393,16 @@ class StreamConfig:
|
|
|
393
393
|
height: int = 640
|
|
394
394
|
target_fps: int = 10
|
|
395
395
|
gpu_id: int = 0
|
|
396
|
+
stream_type: str = "file" # "file" or "rtsp"
|
|
397
|
+
|
|
398
|
+
def __post_init__(self):
|
|
399
|
+
"""Auto-detect stream_type from video_path if not explicitly set."""
|
|
400
|
+
if self.video_path.startswith("rtsp://") or self.video_path.startswith("rtsps://"):
|
|
401
|
+
self.stream_type = "rtsp"
|
|
402
|
+
elif self.video_path.startswith("http://") or self.video_path.startswith("https://"):
|
|
403
|
+
self.stream_type = "file" # Downloaded files
|
|
404
|
+
else:
|
|
405
|
+
self.stream_type = "file"
|
|
396
406
|
|
|
397
407
|
|
|
398
408
|
@dataclass
|
|
@@ -424,6 +434,8 @@ class StreamState:
|
|
|
424
434
|
empty_packets: int = 0
|
|
425
435
|
decode_errors: int = 0 # Consecutive decode errors
|
|
426
436
|
MAX_DECODE_ERRORS: int = 5 # Restart demuxer after this many consecutive errors
|
|
437
|
+
stream_type: str = "file" # "file" or "rtsp"
|
|
438
|
+
source_fps: float = 30.0 # Source video FPS for timestamp calculation
|
|
427
439
|
|
|
428
440
|
|
|
429
441
|
# =============================================================================
|
|
@@ -620,11 +632,20 @@ class NVDECDecoderPool:
|
|
|
620
632
|
logger.info(f"Created NVDEC pool: {self.actual_pool_size}/{pool_size} decoders on GPU {gpu_id}")
|
|
621
633
|
|
|
622
634
|
def assign_stream(self, stream_id: int, camera_id: str, video_path: str,
|
|
623
|
-
width: int = 640, height: int = 640
|
|
635
|
+
width: int = 640, height: int = 640,
|
|
636
|
+
stream_type: str = "file") -> bool:
|
|
624
637
|
"""Assign a stream to a decoder (round-robin).
|
|
625
638
|
|
|
626
639
|
Automatically downloads HTTPS URLs to local files since PyNvVideoCodec's
|
|
627
640
|
bundled FFmpeg doesn't support HTTPS protocol.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
stream_id: Stream identifier
|
|
644
|
+
camera_id: Camera identifier
|
|
645
|
+
video_path: Video file path or RTSP URL
|
|
646
|
+
width: Target width
|
|
647
|
+
height: Target height
|
|
648
|
+
stream_type: Source type ("file" or "rtsp")
|
|
628
649
|
"""
|
|
629
650
|
if self.actual_pool_size == 0:
|
|
630
651
|
return False
|
|
@@ -641,23 +662,40 @@ class NVDECDecoderPool:
|
|
|
641
662
|
logger.error(f"Failed to create demuxer for {camera_id}: {e}")
|
|
642
663
|
return False
|
|
643
664
|
|
|
665
|
+
# Extract source FPS from demuxer for video timestamp calculation
|
|
666
|
+
source_fps = 30.0 # Default fallback
|
|
667
|
+
try:
|
|
668
|
+
# PyNvVideoCodec demuxer provides frame rate info
|
|
669
|
+
fps_num = demuxer.FrameRate()[0]
|
|
670
|
+
fps_den = demuxer.FrameRate()[1]
|
|
671
|
+
if fps_den > 0:
|
|
672
|
+
source_fps = fps_num / fps_den
|
|
673
|
+
logger.debug(f"{camera_id}: Detected source FPS = {source_fps:.2f}")
|
|
674
|
+
except Exception as e:
|
|
675
|
+
logger.debug(f"{camera_id}: Could not get FPS from demuxer, using default: {e}")
|
|
676
|
+
|
|
644
677
|
stream_state = StreamState(
|
|
645
678
|
stream_id=stream_id,
|
|
646
679
|
camera_id=camera_id,
|
|
647
680
|
video_path=local_path, # Store local path for video looping
|
|
648
681
|
demuxer=demuxer,
|
|
649
682
|
width=width,
|
|
650
|
-
height=height
|
|
683
|
+
height=height,
|
|
684
|
+
stream_type=stream_type,
|
|
685
|
+
source_fps=source_fps,
|
|
651
686
|
)
|
|
652
687
|
self.streams_per_decoder[decoder_idx].append(stream_state)
|
|
653
688
|
return True
|
|
654
689
|
|
|
655
690
|
def decode_round(self, decoder_idx: int, frames_per_stream: int = 4,
|
|
656
|
-
target_h: int = 640, target_w: int = 640) -> Tuple[int, List[Tuple[str, cp.ndarray]]]:
|
|
691
|
+
target_h: int = 640, target_w: int = 640) -> Tuple[int, List[Tuple[str, cp.ndarray, int, str]]]:
|
|
657
692
|
"""Decode frames and convert to NV12.
|
|
658
693
|
|
|
659
694
|
Returns:
|
|
660
|
-
(total_frames, [(camera_id, nv12_tensor), ...])
|
|
695
|
+
(total_frames, [(camera_id, nv12_tensor, timestamp_ns, stream_type), ...])
|
|
696
|
+
where:
|
|
697
|
+
- timestamp_ns: For RTSP = UTC nanoseconds, for files = video timestamp ns (frame_num / fps * 1e9)
|
|
698
|
+
- stream_type: "rtsp" or "file"
|
|
661
699
|
"""
|
|
662
700
|
if decoder_idx >= self.actual_pool_size:
|
|
663
701
|
return 0, []
|
|
@@ -676,6 +714,8 @@ class NVDECDecoderPool:
|
|
|
676
714
|
if packet is None:
|
|
677
715
|
stream.demuxer = nvc.CreateDemuxer(stream.video_path)
|
|
678
716
|
stream.empty_packets = 0
|
|
717
|
+
# Reset frame counter on loop for consistent video timestamps
|
|
718
|
+
stream.frames_decoded = 0
|
|
679
719
|
packet = stream.demuxer.Demux()
|
|
680
720
|
if packet is None:
|
|
681
721
|
break
|
|
@@ -685,10 +725,21 @@ class NVDECDecoderPool:
|
|
|
685
725
|
# Wrap decode loop in try/except to catch decode errors
|
|
686
726
|
try:
|
|
687
727
|
for surface in decoder.Decode(packet):
|
|
728
|
+
# Calculate timestamp based on stream type:
|
|
729
|
+
# - RTSP: UTC nanoseconds for real-time sync
|
|
730
|
+
# - File: Video timestamp (frame_num / fps * 1e9) for video playback sync
|
|
731
|
+
if stream.stream_type == "rtsp":
|
|
732
|
+
decode_timestamp_ns = time.time_ns()
|
|
733
|
+
else:
|
|
734
|
+
# Video file: use video timestamp based on frame number
|
|
735
|
+
# This allows syncing to the video timeline instead of wall clock
|
|
736
|
+
video_time_seconds = stream.frames_decoded / stream.source_fps
|
|
737
|
+
decode_timestamp_ns = int(video_time_seconds * 1_000_000_000)
|
|
738
|
+
|
|
688
739
|
tensor = surface_to_nv12(surface, target_h, target_w)
|
|
689
740
|
|
|
690
741
|
if tensor is not None:
|
|
691
|
-
decoded_frames.append((stream.camera_id, tensor))
|
|
742
|
+
decoded_frames.append((stream.camera_id, tensor, decode_timestamp_ns, stream.stream_type))
|
|
692
743
|
frames_this_stream += 1
|
|
693
744
|
stream.frames_decoded += 1
|
|
694
745
|
total_frames += 1
|
|
@@ -839,7 +890,7 @@ def nvdec_pool_worker(
|
|
|
839
890
|
target_w=TARGET_WIDTH # Always 640
|
|
840
891
|
)
|
|
841
892
|
|
|
842
|
-
for cam_id, tensor in decoded_frames:
|
|
893
|
+
for cam_id, tensor, decode_timestamp_ns, stream_type in decoded_frames:
|
|
843
894
|
if cam_id in ring_buffers:
|
|
844
895
|
try:
|
|
845
896
|
# Validate frame shape matches expected NV12 dimensions
|
|
@@ -853,7 +904,11 @@ def nvdec_pool_worker(
|
|
|
853
904
|
)
|
|
854
905
|
continue # Skip this frame
|
|
855
906
|
|
|
856
|
-
|
|
907
|
+
# Pass decode-time timestamp for accurate frame timing
|
|
908
|
+
# For RTSP: UTC nanoseconds, for files: video timestamp ns
|
|
909
|
+
ring_buffers[cam_id].write_frame_fast(
|
|
910
|
+
tensor, sync=False, timestamp_ns=decode_timestamp_ns
|
|
911
|
+
)
|
|
857
912
|
local_frames += 1
|
|
858
913
|
frames_since_counter_update += 1
|
|
859
914
|
|
|
@@ -880,7 +935,7 @@ def nvdec_pool_worker(
|
|
|
880
935
|
# Sync all buffers that received frames in this round
|
|
881
936
|
# Critical for cross-container IPC - ensures GPU writes are visible
|
|
882
937
|
synced_cams = set()
|
|
883
|
-
for cam_id, _ in decoded_frames:
|
|
938
|
+
for cam_id, _, _, _ in decoded_frames:
|
|
884
939
|
if cam_id in ring_buffers and cam_id not in synced_cams:
|
|
885
940
|
ring_buffers[cam_id].sync_writes()
|
|
886
941
|
synced_cams.add(cam_id)
|
|
@@ -1047,7 +1102,8 @@ def nvdec_pool_process(
|
|
|
1047
1102
|
camera_id=config.camera_id,
|
|
1048
1103
|
video_path=config.video_path,
|
|
1049
1104
|
width=TARGET_WIDTH, # Use fixed width
|
|
1050
|
-
height=TARGET_HEIGHT # Use fixed height
|
|
1105
|
+
height=TARGET_HEIGHT, # Use fixed height
|
|
1106
|
+
stream_type=config.stream_type, # Pass stream type for timestamp mode
|
|
1051
1107
|
)
|
|
1052
1108
|
|
|
1053
1109
|
# Write all GPU mappings at once
|
|
File without changes
|
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/matrice_streaming.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/__init__.py
RENAMED
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/client.py
RENAMED
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/client/client_utils.py
RENAMED
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/deployment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{matrice_streaming-0.1.73 → matrice_streaming-0.1.74}/src/matrice_streaming/deployment/todo.txt
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
|
|
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
|
|
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
|