nedo-vision-worker 1.2.0__py3-none-any.whl → 1.2.1__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.
- nedo_vision_worker/__init__.py +1 -1
- nedo_vision_worker/cli.py +1 -1
- nedo_vision_worker/services/VideoStreamClient.py +14 -8
- nedo_vision_worker/services/WorkerSourcePipelineClient.py +7 -6
- nedo_vision_worker/util/FFmpegUtil.py +124 -0
- nedo_vision_worker/util/VideoProbeUtil.py +5 -2
- {nedo_vision_worker-1.2.0.dist-info → nedo_vision_worker-1.2.1.dist-info}/METADATA +1 -1
- {nedo_vision_worker-1.2.0.dist-info → nedo_vision_worker-1.2.1.dist-info}/RECORD +11 -10
- {nedo_vision_worker-1.2.0.dist-info → nedo_vision_worker-1.2.1.dist-info}/WHEEL +0 -0
- {nedo_vision_worker-1.2.0.dist-info → nedo_vision_worker-1.2.1.dist-info}/entry_points.txt +0 -0
- {nedo_vision_worker-1.2.0.dist-info → nedo_vision_worker-1.2.1.dist-info}/top_level.txt +0 -0
nedo_vision_worker/__init__.py
CHANGED
nedo_vision_worker/cli.py
CHANGED
|
@@ -8,6 +8,7 @@ import fractions
|
|
|
8
8
|
from urllib.parse import urlparse
|
|
9
9
|
from .GrpcClientBase import GrpcClientBase
|
|
10
10
|
from .SharedDirectDeviceClient import SharedDirectDeviceClient
|
|
11
|
+
from ..util.FFmpegUtil import get_rtsp_ffmpeg_options, get_rtsp_probe_options
|
|
11
12
|
from ..protos.VisionWorkerService_pb2_grpc import VideoStreamServiceStub
|
|
12
13
|
from ..protos.VisionWorkerService_pb2 import VideoFrame
|
|
13
14
|
|
|
@@ -47,8 +48,10 @@ class VideoStreamClient(GrpcClientBase):
|
|
|
47
48
|
]
|
|
48
49
|
|
|
49
50
|
if stream_type == "rtsp":
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
probe_options = get_rtsp_probe_options()
|
|
52
|
+
# Insert options at the beginning (after ffprobe)
|
|
53
|
+
for i, option in enumerate(probe_options):
|
|
54
|
+
probe_cmd.insert(1 + i, option)
|
|
52
55
|
|
|
53
56
|
result = subprocess.run(probe_cmd, capture_output=True, text=True)
|
|
54
57
|
probe_data = json.loads(result.stdout)
|
|
@@ -97,10 +100,8 @@ class VideoStreamClient(GrpcClientBase):
|
|
|
97
100
|
logging.error(f"Failed to create ffmpeg input for direct device: {e}")
|
|
98
101
|
return
|
|
99
102
|
elif stream_type == "rtsp":
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.input(url, rtsp_transport="tcp", fflags="nobuffer", timeout="5000000")
|
|
103
|
-
)
|
|
103
|
+
rtsp_options = get_rtsp_ffmpeg_options()
|
|
104
|
+
ffmpeg_input = ffmpeg.input(url, **rtsp_options)
|
|
104
105
|
elif stream_type == "hls":
|
|
105
106
|
ffmpeg_input = (
|
|
106
107
|
ffmpeg
|
|
@@ -148,8 +149,13 @@ class VideoStreamClient(GrpcClientBase):
|
|
|
148
149
|
if stream_type == "direct":
|
|
149
150
|
self.shared_device_client.release_device_access(url)
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
try:
|
|
153
|
+
stderr_output = process.stderr.read().decode()
|
|
154
|
+
if stderr_output.strip(): # Only log if there's actual error content
|
|
155
|
+
logging.error(f"FFmpeg stderr for {stream_type} stream: {stderr_output}")
|
|
156
|
+
except Exception as e:
|
|
157
|
+
logging.warning(f"Could not read FFmpeg stderr: {e}")
|
|
158
|
+
|
|
153
159
|
process.terminate()
|
|
154
160
|
process.wait()
|
|
155
161
|
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from ..database.DatabaseManager import _get_storage_paths
|
|
9
9
|
from ..repositories.WorkerSourcePipelineDebugRepository import WorkerSourcePipelineDebugRepository
|
|
10
10
|
from ..repositories.WorkerSourcePipelineDetectionRepository import WorkerSourcePipelineDetectionRepository
|
|
11
|
+
from ..util.FFmpegUtil import get_rtsp_ffmpeg_options, get_stream_timeout_duration
|
|
11
12
|
from .GrpcClientBase import GrpcClientBase
|
|
12
13
|
from .SharedDirectDeviceClient import SharedDirectDeviceClient
|
|
13
14
|
from ..protos.WorkerSourcePipelineService_pb2_grpc import WorkerSourcePipelineServiceStub
|
|
@@ -194,10 +195,8 @@ class WorkerSourcePipelineClient(GrpcClientBase):
|
|
|
194
195
|
logging.error(f"Error setting up direct device {device_index}: {e}")
|
|
195
196
|
return None
|
|
196
197
|
elif stream_type == "rtsp":
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
.input(url, rtsp_transport="tcp", fflags="nobuffer", timeout="5000000")
|
|
200
|
-
)
|
|
198
|
+
rtsp_options = get_rtsp_ffmpeg_options()
|
|
199
|
+
ffmpeg_input = ffmpeg.input(url, **rtsp_options)
|
|
201
200
|
elif stream_type == "hls":
|
|
202
201
|
ffmpeg_input = (
|
|
203
202
|
ffmpeg
|
|
@@ -246,11 +245,13 @@ class WorkerSourcePipelineClient(GrpcClientBase):
|
|
|
246
245
|
)
|
|
247
246
|
|
|
248
247
|
try:
|
|
249
|
-
|
|
248
|
+
# Use appropriate timeout for different stream types
|
|
249
|
+
timeout_duration = get_stream_timeout_duration(stream_type)
|
|
250
|
+
stdout, stderr = process.communicate(timeout=timeout_duration)
|
|
250
251
|
|
|
251
252
|
if process.returncode != 0:
|
|
252
253
|
error_msg = stderr.decode('utf-8', errors='ignore')
|
|
253
|
-
logging.error(f"FFmpeg error: {error_msg}")
|
|
254
|
+
logging.error(f"FFmpeg error for {stream_type} stream: {error_msg}")
|
|
254
255
|
return None
|
|
255
256
|
|
|
256
257
|
if not stdout:
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FFmpeg utilities for Jetson compatibility and RTSP stream handling.
|
|
3
|
+
|
|
4
|
+
This module provides common FFmpeg configurations and utilities that work
|
|
5
|
+
reliably on Jetson devices with FFmpeg 4.4.1 and newer versions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import subprocess
|
|
10
|
+
import re
|
|
11
|
+
from typing import Dict, Any, Tuple
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_ffmpeg_version() -> Tuple[int, int, int]:
|
|
15
|
+
"""
|
|
16
|
+
Get the FFmpeg version as a tuple of (major, minor, patch).
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Tuple[int, int, int]: Version tuple (major, minor, patch)
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
result = subprocess.run(['ffmpeg', '-version'], capture_output=True, text=True, timeout=5)
|
|
23
|
+
if result.returncode == 0:
|
|
24
|
+
# Extract version from output like "ffmpeg version n7.1.1" or "ffmpeg version 4.4.1"
|
|
25
|
+
match = re.search(r'ffmpeg version n?(\d+)\.(\d+)\.(\d+)', result.stdout)
|
|
26
|
+
if match:
|
|
27
|
+
return (int(match.group(1)), int(match.group(2)), int(match.group(3)))
|
|
28
|
+
except Exception as e:
|
|
29
|
+
logging.warning(f"Could not determine FFmpeg version: {e}")
|
|
30
|
+
|
|
31
|
+
# Default to a reasonable version if detection fails
|
|
32
|
+
return (4, 4, 1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_rtsp_ffmpeg_options() -> Dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Get FFmpeg options optimized for RTSP streams with version compatibility.
|
|
38
|
+
|
|
39
|
+
These options work across different FFmpeg versions:
|
|
40
|
+
- FFmpeg 4.4.x: Uses stimeout
|
|
41
|
+
- FFmpeg 5.x+: Uses timeout
|
|
42
|
+
- FFmpeg 7.x+: Uses timeout
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Dict[str, Any]: FFmpeg input options for RTSP streams
|
|
46
|
+
"""
|
|
47
|
+
version = get_ffmpeg_version()
|
|
48
|
+
major, minor, patch = version
|
|
49
|
+
|
|
50
|
+
# Base options that work across all versions
|
|
51
|
+
options = {
|
|
52
|
+
"rtsp_transport": "tcp",
|
|
53
|
+
"fflags": "nobuffer+genpts",
|
|
54
|
+
"max_delay": "5000000", # Max buffering delay
|
|
55
|
+
"buffer_size": "1024000", # Input buffer size
|
|
56
|
+
"avoid_negative_ts": "make_zero" # Handle timestamp issues
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Add version-specific timeout option
|
|
60
|
+
if major == 4 and minor == 4:
|
|
61
|
+
# FFmpeg 4.4.x uses stimeout
|
|
62
|
+
options["stimeout"] = "5000000"
|
|
63
|
+
logging.debug("Using stimeout for FFmpeg 4.4.x")
|
|
64
|
+
else:
|
|
65
|
+
# FFmpeg 5.x+ uses timeout (microseconds)
|
|
66
|
+
options["timeout"] = "5000000"
|
|
67
|
+
logging.debug(f"Using timeout for FFmpeg {major}.{minor}.{patch}")
|
|
68
|
+
|
|
69
|
+
return options
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_rtsp_probe_options() -> list:
|
|
73
|
+
"""
|
|
74
|
+
Get ffprobe command line options for RTSP streams with version compatibility.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
list: Command line options to insert into ffprobe command
|
|
78
|
+
"""
|
|
79
|
+
version = get_ffmpeg_version()
|
|
80
|
+
major, minor, patch = version
|
|
81
|
+
|
|
82
|
+
base_options = ["-rtsp_transport", "tcp"]
|
|
83
|
+
|
|
84
|
+
# Add version-specific timeout option
|
|
85
|
+
if major == 4 and minor == 4:
|
|
86
|
+
# FFmpeg 4.4.x uses stimeout
|
|
87
|
+
return base_options + ["-stimeout", "5000000"]
|
|
88
|
+
else:
|
|
89
|
+
# FFmpeg 5.x+ uses timeout
|
|
90
|
+
return base_options + ["-timeout", "5000000"]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def log_ffmpeg_version_info():
|
|
94
|
+
"""Log information about FFmpeg compatibility."""
|
|
95
|
+
version = get_ffmpeg_version()
|
|
96
|
+
major, minor, patch = version
|
|
97
|
+
|
|
98
|
+
logging.info(f"Detected FFmpeg version: {major}.{minor}.{patch}")
|
|
99
|
+
|
|
100
|
+
if major == 4 and minor == 4:
|
|
101
|
+
logging.info("Using 'stimeout' parameter for FFmpeg 4.4.x compatibility")
|
|
102
|
+
else:
|
|
103
|
+
logging.info(f"Using 'timeout' parameter for FFmpeg {major}.{minor}.{patch}")
|
|
104
|
+
|
|
105
|
+
logging.info("RTSP configuration optimized for embedded devices")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_stream_timeout_duration(stream_type: str) -> int:
|
|
109
|
+
"""
|
|
110
|
+
Get appropriate timeout duration for different stream types.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
stream_type (str): Type of stream (rtsp, hls, direct, etc.)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
int: Timeout duration in seconds
|
|
117
|
+
"""
|
|
118
|
+
timeouts = {
|
|
119
|
+
"rtsp": 30, # RTSP streams may take longer to connect
|
|
120
|
+
"hls": 20, # HLS streams need time for manifest download
|
|
121
|
+
"direct": 10, # Direct device access should be faster
|
|
122
|
+
"video_file": 5 # Local files should be very fast
|
|
123
|
+
}
|
|
124
|
+
return timeouts.get(stream_type, 15) # Default 15 seconds
|
|
@@ -8,6 +8,7 @@ import sys
|
|
|
8
8
|
import platform
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from urllib.parse import urlparse
|
|
11
|
+
from .FFmpegUtil import get_rtsp_probe_options
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
try:
|
|
@@ -289,8 +290,10 @@ class VideoProbeUtil:
|
|
|
289
290
|
"-show_entries", "stream=width,height,avg_frame_rate", "-of", "json"]
|
|
290
291
|
|
|
291
292
|
if stream_type == "rtsp":
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
probe_options = get_rtsp_probe_options()
|
|
294
|
+
# Insert options at the beginning (after ffprobe)
|
|
295
|
+
for i, option in enumerate(probe_options):
|
|
296
|
+
cmd.insert(1 + i, option)
|
|
294
297
|
|
|
295
298
|
cmd.append(video_url)
|
|
296
299
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedo-vision-worker
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Nedo Vision Worker Service Library for AI Vision Processing
|
|
5
5
|
Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
6
6
|
Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
nedo_vision_worker/__init__.py,sha256=
|
|
2
|
-
nedo_vision_worker/cli.py,sha256=
|
|
1
|
+
nedo_vision_worker/__init__.py,sha256=OuQTi_CjaQDpS1kVellEttgj1dkluSOCwxE0teArbyY,203
|
|
2
|
+
nedo_vision_worker/cli.py,sha256=ddWspJmSgVkcUYvRdkvTtMNuMTDvNCqLLuMVU9KE3Ik,7457
|
|
3
3
|
nedo_vision_worker/doctor.py,sha256=uZ-NM_PfaTG5CG5OWFnl7cEsOTBWMGXNuKWuTV07deg,47228
|
|
4
4
|
nedo_vision_worker/worker_service.py,sha256=rXUVmyxcJPGhQEZ4UQvjQS5UqlnLBYudHQZCj0dQDxo,10421
|
|
5
5
|
nedo_vision_worker/config/ConfigurationManager.py,sha256=QrQaQ9Cdjpkcr2JE_miyrWJIZmMgZwJYBz-wE45Zzes,8011
|
|
@@ -63,18 +63,19 @@ nedo_vision_worker/services/SharedVideoStreamServer.py,sha256=WMKVxkzMoyfbgYiJ0f
|
|
|
63
63
|
nedo_vision_worker/services/SystemUsageClient.py,sha256=PbRuwDWKnMarcnkGtOKfYB5nA-3DeKv7V5_hZr8EDEo,3200
|
|
64
64
|
nedo_vision_worker/services/SystemWideDeviceCoordinator.py,sha256=9zBJMCbTMZS7gwN67rFpoUiTr82El2rpIO7DKFzeMjM,9417
|
|
65
65
|
nedo_vision_worker/services/VideoSharingDaemon.py,sha256=hYMjUIKNUVT1qSxuUuHN-7Bd85MDkxfqslxDLe2PBYQ,29721
|
|
66
|
-
nedo_vision_worker/services/VideoStreamClient.py,sha256=
|
|
66
|
+
nedo_vision_worker/services/VideoStreamClient.py,sha256=QSgUV3LijYrNdnBG1ylABOdUaSatQamfXaqJhAiol9M,7260
|
|
67
67
|
nedo_vision_worker/services/WorkerSourceClient.py,sha256=vDZeCuHL5QQ2-knZ4TOSA59jzmbbThGIwFKKLEZ72Ws,9198
|
|
68
|
-
nedo_vision_worker/services/WorkerSourcePipelineClient.py,sha256=
|
|
68
|
+
nedo_vision_worker/services/WorkerSourcePipelineClient.py,sha256=amhZOua0lmlWl7ZkU-SSbT2E1Y5D-uKde9bDCVxOqM4,17924
|
|
69
69
|
nedo_vision_worker/services/WorkerSourceUpdater.py,sha256=MsUsKL75sXj2odCcbupkFDW0KXg9LSu6-67iMWpYkHs,7679
|
|
70
70
|
nedo_vision_worker/services/WorkerStatusClient.py,sha256=7kC5EZjEBwWtHOE6UQ29OPCpYnv_6HSuH7Tc0alK_2Q,2531
|
|
71
71
|
nedo_vision_worker/services/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
72
|
+
nedo_vision_worker/util/FFmpegUtil.py,sha256=_TRA_Q2CAKatrbhOMKG5Uhhk6FrMJ6sbwMnd0Qwq0co,4086
|
|
72
73
|
nedo_vision_worker/util/HardwareID.py,sha256=rSW8-6stm7rjXEdkYGqXMUn56gyw62YiWnSwZQVCCLM,4315
|
|
73
74
|
nedo_vision_worker/util/ImageUploader.py,sha256=2xipN3fwpKgFmbvoGIdElpGn5ARJyrgR4dXtbRf73hw,3764
|
|
74
75
|
nedo_vision_worker/util/Networking.py,sha256=uOtL8HkKZXJp02ZZIHWYMAvAsaYb7BsAPTncfdvJx2c,3241
|
|
75
76
|
nedo_vision_worker/util/PlatformDetector.py,sha256=-iLPrKs7hp_oltkCI3hESJQkC2uRyu1-8mAbZrvgWx0,1501
|
|
76
77
|
nedo_vision_worker/util/SystemMonitor.py,sha256=2MWYaEXoL91UANT_-SuDWrFMq1ajPorh8co6Py9PV_c,11300
|
|
77
|
-
nedo_vision_worker/util/VideoProbeUtil.py,sha256=
|
|
78
|
+
nedo_vision_worker/util/VideoProbeUtil.py,sha256=1ViUjt6NSMilquapHf6XC0h93OwhA40LwGSnYOtpMh0,13362
|
|
78
79
|
nedo_vision_worker/util/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
79
80
|
nedo_vision_worker/worker/CoreActionWorker.py,sha256=lb7zPY3yui6I3F4rX4Ii7JwpWZahLEO72rh3iWOgFmg,5441
|
|
80
81
|
nedo_vision_worker/worker/DataSenderWorker.py,sha256=o32MT28EqYwAPmd9NKhX3rfNlDKekNOI2n8mZ6s8CpU,7162
|
|
@@ -90,8 +91,8 @@ nedo_vision_worker/worker/SystemUsageManager.py,sha256=StutV4UyLUfduYfK20de4SbPd
|
|
|
90
91
|
nedo_vision_worker/worker/VideoStreamWorker.py,sha256=5n6v1PNO7IB-jj_McALLkUP-cBjJoIEw4UiSAs3vTb0,7606
|
|
91
92
|
nedo_vision_worker/worker/WorkerManager.py,sha256=T0vMfhOd7YesgQ9o2w6soeJ6n9chUAcuwcGe7p31xr0,5298
|
|
92
93
|
nedo_vision_worker/worker/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
93
|
-
nedo_vision_worker-1.2.
|
|
94
|
-
nedo_vision_worker-1.2.
|
|
95
|
-
nedo_vision_worker-1.2.
|
|
96
|
-
nedo_vision_worker-1.2.
|
|
97
|
-
nedo_vision_worker-1.2.
|
|
94
|
+
nedo_vision_worker-1.2.1.dist-info/METADATA,sha256=GUzjnngaGQ7i48oY3BMpdPB701o8_n2LxwlayNJb3uw,14591
|
|
95
|
+
nedo_vision_worker-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
96
|
+
nedo_vision_worker-1.2.1.dist-info/entry_points.txt,sha256=LrglS-8nCi8C_PL_pa6uxdgCe879hBETHDVXAckvs-8,60
|
|
97
|
+
nedo_vision_worker-1.2.1.dist-info/top_level.txt,sha256=vgilhlkyD34YsEKkaBabmhIpcKSvF3XpzD2By68L-XI,19
|
|
98
|
+
nedo_vision_worker-1.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|