Skip to content

Commit a5db576

Browse files
Nicholas VergunstNicholas Vergunst
authored andcommitted
ENT-1686/1687: Harden OpenCV RTSPS capture opens
Add FFmpeg open timeout to bound global TLS lock hold, pin CAP_FFMPEG for rtsps:// URLs, and log when allow_self_signed maps to full bypass on the OpenCV path.
1 parent b3d41ea commit a5db576

3 files changed

Lines changed: 21 additions & 4 deletions

File tree

inference/core/interfaces/camera/rtsp_opencv_tls.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import logging
56
import os
67
import threading
78
from contextlib import contextmanager
@@ -15,6 +16,10 @@
1516

1617
OPENCV_FFMPEG_CAPTURE_OPTIONS_ENV_VAR = "OPENCV_FFMPEG_CAPTURE_OPTIONS"
1718
SSL_CERT_FILE_ENV_VAR = "SSL_CERT_FILE"
19+
# FFmpeg RTSP socket timeout (microseconds). Bounds global-lock hold during capture open.
20+
_RTSPS_OPEN_TIMEOUT_US = 5_000_000
21+
22+
logger = logging.getLogger(__name__)
1823

1924
_opencv_rtsps_tls_lock = threading.Lock()
2025

@@ -48,6 +53,11 @@ def _build_rtsps_tls_option_overrides() -> Dict[str, str]:
4853
# rfdm maps allow_self_signed to 126 (unknown-CA only). FFmpeg only
4954
# supports full verify on/off, so treat both 0 and 126 as tls_verify;0.
5055
if stripped in {"0", "126"}:
56+
if stripped == "126":
57+
logger.warning(
58+
"OpenCV/FFmpeg RTSPS cannot enforce partial TLS validation; "
59+
"tls_validation_flags=126 (allow_self_signed) maps to tls_verify=0"
60+
)
5161
overrides["tls_verify"] = "0"
5262
else:
5363
overrides["tls_verify"] = "1"
@@ -73,10 +83,13 @@ def build_opencv_ffmpeg_capture_options(video: str) -> Optional[str]:
7383
return None
7484

7585
overrides = _build_rtsps_tls_option_overrides()
76-
return merge_opencv_ffmpeg_capture_options(
77-
os.environ.get(OPENCV_FFMPEG_CAPTURE_OPTIONS_ENV_VAR),
78-
overrides,
79-
)
86+
existing = os.environ.get(OPENCV_FFMPEG_CAPTURE_OPTIONS_ENV_VAR)
87+
merged = _parse_opencv_ffmpeg_capture_options(existing or "")
88+
for key, value in overrides.items():
89+
merged[key] = value
90+
if "stimeout" not in merged and "timeout" not in merged:
91+
merged["stimeout"] = str(_RTSPS_OPEN_TIMEOUT_US)
92+
return _format_opencv_ffmpeg_capture_options(merged)
8093

8194

8295
@contextmanager

inference/core/interfaces/camera/video_source.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
StreamOperationNotAllowedError,
3636
)
3737
from inference.core.interfaces.camera.rtsp_opencv_tls import opencv_rtsps_tls_env
38+
from inference.core.interfaces.camera.rtsp_tls import is_rtsps_url
3839
from inference.core.interfaces.camera.source_reference_sanitizer import (
3940
sanitize_source_reference,
4041
)
@@ -150,6 +151,8 @@ def __init__(self, video: Union[str, int]):
150151
with opencv_rtsps_tls_env(video), capture_process_stderr() as captured_stderr:
151152
if _consumes_camera_on_jetson(video=video):
152153
self.stream = cv2.VideoCapture(video, cv2.CAP_V4L2)
154+
elif isinstance(video, str) and is_rtsps_url(video):
155+
self.stream = cv2.VideoCapture(video, cv2.CAP_FFMPEG)
153156
else:
154157
self.stream = cv2.VideoCapture(video)
155158
self._connection_error_message = extract_stream_open_error(

tests/inference/unit_tests/core/interfaces/camera/test_rtsp_opencv_tls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def test_build_options_rtsps_default_strict_verify(
3030
assert got is not None
3131
assert "rtsp_transport;tcp" in got
3232
assert "tls_verify;1" in got
33+
assert "stimeout;5000000" in got
3334

3435

3536
def test_build_options_rtsps_with_ca(monkeypatch: pytest.MonkeyPatch) -> None:

0 commit comments

Comments
 (0)