python-aidot-cameras 0.12.11__tar.gz → 0.12.13__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.
- {python_aidot_cameras-0.12.11/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.13}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/client.py +50 -2
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/sdes_open.py +11 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/pyproject.toml +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/python_aidot_cameras.egg-info/SOURCES.txt +2 -0
- python_aidot_cameras-0.12.13/tests/test_dormant_camera_cleanup.py +65 -0
- python_aidot_cameras-0.12.13/tests/test_viewer_check_throttle.py +70 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/LICENSE +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/README.md +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/setup.cfg +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_wait_or_event.py +0 -0
{python_aidot_cameras-0.12.11/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.13}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.13
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/client.py
RENAMED
|
@@ -148,6 +148,13 @@ _BG_TASKS: set = set()
|
|
|
148
148
|
# means refetching is not helping, so back off rather than loop.
|
|
149
149
|
_MQTT_CREDENTIAL_REFETCH_FLOOR = 30.0
|
|
150
150
|
|
|
151
|
+
# How often the "is anyone watching" question may actually reach go2rtc. The
|
|
152
|
+
# watchdog loops tick twice a second; asking that often opens a fresh HTTP
|
|
153
|
+
# session per camera per tick against the service that also has to serve the
|
|
154
|
+
# video. Idle release is measured in minutes, so seconds of staleness here costs
|
|
155
|
+
# nothing.
|
|
156
|
+
_VIEWER_CHECK_INTERVAL_S = 10.0
|
|
157
|
+
|
|
151
158
|
|
|
152
159
|
def _spawn_bg(coro):
|
|
153
160
|
_t = asyncio.ensure_future(coro)
|
|
@@ -2937,6 +2944,10 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2937
2944
|
register, deregister and viewer-count paths cannot drift apart."""
|
|
2938
2945
|
return f"aidot_{self.device_id[:12]}"
|
|
2939
2946
|
|
|
2947
|
+
# Cached answer to "is anyone watching", so the watchdog loops can ask on
|
|
2948
|
+
# every tick without hammering go2rtc.
|
|
2949
|
+
_viewer_cache: "tuple[float, Optional[bool]]" = (0.0, None)
|
|
2950
|
+
|
|
2940
2951
|
async def _viewer_present(self, port: int) -> "Optional[bool]":
|
|
2941
2952
|
"""Is anyone ACTUALLY watching? True / False / None (unknown).
|
|
2942
2953
|
|
|
@@ -2952,6 +2963,17 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2952
2963
|
Falls back to the TCP-table check when go2rtc is not in use, and returns
|
|
2953
2964
|
None (never release) if neither can answer.
|
|
2954
2965
|
"""
|
|
2966
|
+
# THROTTLE FIRST. The DTLS watchdog calls this every 0.5s and the SDES
|
|
2967
|
+
# one on a similar cadence, and the go2rtc query below opens a fresh HTTP
|
|
2968
|
+
# session each time - so an unthrottled check is two requests per second
|
|
2969
|
+
# per camera, forever, against the very service that has to serve the
|
|
2970
|
+
# video. On a small fleet that is enough load to stop go2rtc answering
|
|
2971
|
+
# at all, which looks exactly like "no video anywhere".
|
|
2972
|
+
_now_mono = time.monotonic()
|
|
2973
|
+
_last, _cached = self._viewer_cache
|
|
2974
|
+
if _now_mono - _last < _VIEWER_CHECK_INTERVAL_S:
|
|
2975
|
+
return _cached
|
|
2976
|
+
|
|
2955
2977
|
name = self._go2rtc_stream_name()
|
|
2956
2978
|
base = self._go2rtc_url
|
|
2957
2979
|
if not base:
|
|
@@ -2968,6 +2990,7 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2968
2990
|
"no go2rtc url and the serve is an RTSP push to a shared "
|
|
2969
2991
|
"port; cannot tell whether anyone is watching",
|
|
2970
2992
|
)
|
|
2993
|
+
self._viewer_cache = (_now_mono, None)
|
|
2971
2994
|
return None
|
|
2972
2995
|
if base and name:
|
|
2973
2996
|
try:
|
|
@@ -2977,10 +3000,14 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
2977
3000
|
async with aiohttp.ClientSession() as _s:
|
|
2978
3001
|
viewers = await Go2rtcClient(_s, base).viewer_count(name)
|
|
2979
3002
|
if viewers is not None:
|
|
2980
|
-
|
|
3003
|
+
_answer = viewers > 0
|
|
3004
|
+
self._viewer_cache = (_now_mono, _answer)
|
|
3005
|
+
return _answer
|
|
2981
3006
|
except Exception as exc:
|
|
2982
3007
|
_LOGGER.debug("go2rtc viewer check failed: %s", exc)
|
|
2983
|
-
|
|
3008
|
+
_answer = self._sdes_serve_consumer_present(port)
|
|
3009
|
+
self._viewer_cache = (_now_mono, _answer)
|
|
3010
|
+
return _answer
|
|
2984
3011
|
|
|
2985
3012
|
async def _sdes_keepalive_loop(self) -> None:
|
|
2986
3013
|
"""Keep the cold-start serve relay alive around the SDES keepalive loop.
|
|
@@ -3128,6 +3155,19 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3128
3155
|
self._cancel_keepalive_renew()
|
|
3129
3156
|
self._keepalive_rtsp_url = None
|
|
3130
3157
|
self._serve_ready.clear()
|
|
3158
|
+
# Take the stream out of go2rtc too. Leaving it registered
|
|
3159
|
+
# points go2rtc at a source that no longer exists, so a viewer
|
|
3160
|
+
# attaching while the camera is dormant gets a hard refusal
|
|
3161
|
+
# ("connection refused" on the serve port) instead of a clean
|
|
3162
|
+
# "no such stream" - and in push mode that source was never
|
|
3163
|
+
# dialable in the first place. stream_source() re-registers on
|
|
3164
|
+
# the next view.
|
|
3165
|
+
try:
|
|
3166
|
+
await self._deregister_go2rtc()
|
|
3167
|
+
except Exception:
|
|
3168
|
+
_LOGGER.debug("camera %s: swallowed exception in %s",
|
|
3169
|
+
getattr(self, "device_id", "?"),
|
|
3170
|
+
"_deregister_go2rtc", exc_info=True)
|
|
3131
3171
|
_LOGGER.debug(
|
|
3132
3172
|
"SDES serve: %s idle (no viewer) - released until next view",
|
|
3133
3173
|
self.device_id,
|
|
@@ -4089,6 +4129,14 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
4089
4129
|
self._cancel_keepalive_renew()
|
|
4090
4130
|
self._keepalive_rtsp_url = None
|
|
4091
4131
|
self._serve_ready.clear()
|
|
4132
|
+
# Same as the SDES path: do not leave go2rtc holding a stream
|
|
4133
|
+
# whose source no longer exists.
|
|
4134
|
+
try:
|
|
4135
|
+
await self._deregister_go2rtc()
|
|
4136
|
+
except Exception:
|
|
4137
|
+
_LOGGER.debug("camera %s: swallowed exception in %s",
|
|
4138
|
+
getattr(self, "device_id", "?"),
|
|
4139
|
+
"_deregister_go2rtc", exc_info=True)
|
|
4092
4140
|
_LOGGER.debug(
|
|
4093
4141
|
"DTLS serve: %s idle (no viewer) - released until next view",
|
|
4094
4142
|
self.device_id,
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/sdes_open.py
RENAMED
|
@@ -102,6 +102,11 @@ def _sdes_echo_wait_timeout(skip_liveplay: bool) -> float:
|
|
|
102
102
|
return 0.0 if skip_liveplay else 2.0
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
# ffmpeg returns AVERROR(EPIPE) = -32 when its output consumer disappears, and a
|
|
106
|
+
# process exit status is truncated to an unsigned byte: -32 & 0xFF = 224.
|
|
107
|
+
_FFMPEG_EXIT_EPIPE = 224
|
|
108
|
+
|
|
109
|
+
|
|
105
110
|
def _classify_ffmpeg_exit(rc: int, teardown_requested: bool) -> int:
|
|
106
111
|
"""Log level for the bridge observe loop's "ffmpeg exited" line.
|
|
107
112
|
|
|
@@ -116,6 +121,12 @@ def _classify_ffmpeg_exit(rc: int, teardown_requested: bool) -> int:
|
|
|
116
121
|
"""
|
|
117
122
|
if rc < 0 and teardown_requested:
|
|
118
123
|
return logging.DEBUG
|
|
124
|
+
if rc == _FFMPEG_EXIT_EPIPE:
|
|
125
|
+
# Broken pipe: the consumer went away. That is the NORMAL end of a
|
|
126
|
+
# `-listen 1` serve - go2rtc disconnects and ffmpeg exits - so warning
|
|
127
|
+
# about it trains the reader to ignore real failures. A process exit
|
|
128
|
+
# status is an unsigned byte, so AVERROR(EPIPE) = -32 surfaces as 224.
|
|
129
|
+
return logging.DEBUG
|
|
119
130
|
return logging.WARNING
|
|
120
131
|
|
|
121
132
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.12.
|
|
7
|
+
version = "0.12.13"
|
|
8
8
|
description = "Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13/python_aidot_cameras.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.13
|
|
4
4
|
Summary: Control AiDot/Leedarson WiFi lights and cameras (WebRTC streaming, two-way audio, PTZ, controls)
|
|
5
5
|
Author-email: cbrightly <chris.brightly@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -72,6 +72,7 @@ tests/test_backoff.py
|
|
|
72
72
|
tests/test_deferred_hardening.py
|
|
73
73
|
tests/test_device_login_guard.py
|
|
74
74
|
tests/test_device_user_info_cache.py
|
|
75
|
+
tests/test_dormant_camera_cleanup.py
|
|
75
76
|
tests/test_dtls_not_ready_burst.py
|
|
76
77
|
tests/test_dtls_pinning.py
|
|
77
78
|
tests/test_dtls_serve_decode_skip.py
|
|
@@ -137,5 +138,6 @@ tests/test_terminal_ack.py
|
|
|
137
138
|
tests/test_token_file_robustness.py
|
|
138
139
|
tests/test_token_refresh.py
|
|
139
140
|
tests/test_upstream_compat.py
|
|
141
|
+
tests/test_viewer_check_throttle.py
|
|
140
142
|
tests/test_viewer_presence.py
|
|
141
143
|
tests/test_wait_or_event.py
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""A dormant camera must not leave a dead stream registered in go2rtc.
|
|
2
|
+
|
|
3
|
+
Idle-release stops the serve but used to leave the go2rtc stream in place,
|
|
4
|
+
pointing at a source that no longer exists. A viewer attaching then gets a hard
|
|
5
|
+
"connection refused" on the serve port rather than a clean miss - and in PUSH mode
|
|
6
|
+
that source was never dialable at all, because the keepalive publishes INTO go2rtc
|
|
7
|
+
rather than serving over HTTP.
|
|
8
|
+
|
|
9
|
+
Observed live: four go2rtc streams registered against serve ports with nothing
|
|
10
|
+
listening, and go2rtc reporting
|
|
11
|
+
`dial tcp 127.0.0.1:18981: connect: connection refused`.
|
|
12
|
+
"""
|
|
13
|
+
import inspect
|
|
14
|
+
|
|
15
|
+
import aidot_cameras.camera.client as cc
|
|
16
|
+
from aidot_cameras.camera.sdes_open import (
|
|
17
|
+
_FFMPEG_EXIT_EPIPE,
|
|
18
|
+
_classify_ffmpeg_exit,
|
|
19
|
+
)
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _src(name):
|
|
24
|
+
return inspect.getsource(getattr(cc.CameraMixin, name))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_sdes_idle_release_deregisters_from_go2rtc():
|
|
28
|
+
src = _src("_sdes_keepalive_loop_inner")
|
|
29
|
+
idle = src[src.index("if _idle_release:"):]
|
|
30
|
+
assert "_deregister_go2rtc" in idle
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_dtls_idle_release_deregisters_from_go2rtc():
|
|
34
|
+
src = _src("_dtls_serve_loop_inner")
|
|
35
|
+
idle = src[src.index("if idle_release:"):]
|
|
36
|
+
assert "_deregister_go2rtc" in idle
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_deregistration_failure_cannot_break_the_release():
|
|
40
|
+
# Going dormant must still complete if go2rtc is unreachable.
|
|
41
|
+
for name, marker in (("_sdes_keepalive_loop_inner", "if _idle_release:"),
|
|
42
|
+
("_dtls_serve_loop_inner", "if idle_release:")):
|
|
43
|
+
idle = _src(name)
|
|
44
|
+
idle = idle[idle.index(marker):]
|
|
45
|
+
block = idle[idle.index("_deregister_go2rtc") - 200:
|
|
46
|
+
idle.index("_deregister_go2rtc") + 300]
|
|
47
|
+
assert "try:" in block and "except Exception" in block
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_consumer_disconnect_is_not_logged_as_a_failure():
|
|
51
|
+
# ffmpeg returns AVERROR(EPIPE) = -32 when its consumer goes away, and an exit
|
|
52
|
+
# status is an unsigned byte, so it surfaces as 224. That is the normal end of
|
|
53
|
+
# a `-listen 1` serve, not a fault.
|
|
54
|
+
assert _FFMPEG_EXIT_EPIPE == 224
|
|
55
|
+
assert _classify_ffmpeg_exit(224, teardown_requested=False) == logging.DEBUG
|
|
56
|
+
assert _classify_ffmpeg_exit(224, teardown_requested=True) == logging.DEBUG
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_real_failures_still_warn():
|
|
60
|
+
assert _classify_ffmpeg_exit(1, teardown_requested=False) == logging.WARNING
|
|
61
|
+
assert _classify_ffmpeg_exit(255, teardown_requested=False) == logging.WARNING
|
|
62
|
+
# A signal death with no teardown in flight is still unexpected.
|
|
63
|
+
assert _classify_ffmpeg_exit(-9, teardown_requested=False) == logging.WARNING
|
|
64
|
+
# ...but an expected teardown is quiet.
|
|
65
|
+
assert _classify_ffmpeg_exit(-9, teardown_requested=True) == logging.DEBUG
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""The viewer check must not be asked of go2rtc on every watchdog tick.
|
|
2
|
+
|
|
3
|
+
The DTLS watchdog loops twice a second, and the go2rtc query opens a fresh HTTP
|
|
4
|
+
session per call - so an unthrottled check is two requests per second per camera,
|
|
5
|
+
forever, aimed at the very service that also has to serve the video. On a small
|
|
6
|
+
fleet that was enough to stop go2rtc answering at all, which presents as no video
|
|
7
|
+
anywhere. Idle release is measured in minutes, so a few seconds of staleness in
|
|
8
|
+
the answer costs nothing.
|
|
9
|
+
"""
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
import aidot_cameras.camera.client as cc
|
|
13
|
+
from aidot_cameras.device_client import CameraDeviceClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _client(answer):
|
|
17
|
+
c = CameraDeviceClient.__new__(CameraDeviceClient)
|
|
18
|
+
c._go2rtc_url = None
|
|
19
|
+
c._keepalive_rtsp_url = "http://127.0.0.1:18931/x.ts"
|
|
20
|
+
c.device_id = "abc123abc123def"
|
|
21
|
+
c._viewer_cache = (0.0, None)
|
|
22
|
+
calls = []
|
|
23
|
+
|
|
24
|
+
def _probe(port):
|
|
25
|
+
calls.append(port)
|
|
26
|
+
return answer
|
|
27
|
+
|
|
28
|
+
c._sdes_serve_consumer_present = _probe
|
|
29
|
+
return c, calls
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_repeated_calls_hit_the_backend_once():
|
|
33
|
+
c, calls = _client(True)
|
|
34
|
+
|
|
35
|
+
async def go():
|
|
36
|
+
for _ in range(40): # 40 watchdog ticks
|
|
37
|
+
await c._viewer_present(18931)
|
|
38
|
+
asyncio.run(go())
|
|
39
|
+
assert len(calls) == 1, f"backend hit {len(calls)} times, expected 1"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_the_cached_answer_is_returned_verbatim():
|
|
43
|
+
for answer in (True, False, None):
|
|
44
|
+
c, _ = _client(answer)
|
|
45
|
+
|
|
46
|
+
async def go():
|
|
47
|
+
first = await c._viewer_present(18931)
|
|
48
|
+
second = await c._viewer_present(18931)
|
|
49
|
+
return first, second
|
|
50
|
+
first, second = asyncio.run(go())
|
|
51
|
+
assert first == answer and second == answer
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_the_cache_expires_so_the_answer_can_change():
|
|
55
|
+
c, calls = _client(True)
|
|
56
|
+
|
|
57
|
+
async def go():
|
|
58
|
+
await c._viewer_present(18931)
|
|
59
|
+
# Pretend the interval has elapsed.
|
|
60
|
+
stamp, cached = c._viewer_cache
|
|
61
|
+
c._viewer_cache = (stamp - cc._VIEWER_CHECK_INTERVAL_S - 1, cached)
|
|
62
|
+
await c._viewer_present(18931)
|
|
63
|
+
asyncio.run(go())
|
|
64
|
+
assert len(calls) == 2
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_the_interval_is_long_enough_to_matter():
|
|
68
|
+
# The watchdog ticks every 0.5s; anything under a couple of seconds would
|
|
69
|
+
# still be a request storm.
|
|
70
|
+
assert cc._VIEWER_CHECK_INTERVAL_S >= 5.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/clock.py
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
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rate.py
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
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/stats.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/_vendor/aiortc/utils.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/go2rtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/models.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/webrtc.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/camera/webrtc_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/aidot_cameras/device_client.py
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
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_serve_open_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_dtls_skip_signaling_wait.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_ice_config_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_idle_release_both_loops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_keyframe_prompter.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_light_active_color_mode.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_live_stream_param.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_credential_refetch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_mqtt_session_reconnect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_offline_keepalive_pause.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_open_fail_logger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_bridge_break_gate.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_echo_wait_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_fast_liveplay.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_ffmpeg_stderr_log.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_idle_release.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_open_cleanup.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_serve_audio.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_sdes_teardown_exit_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_security_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_serializable_login_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_stream_drain_ownership.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.11 → python_aidot_cameras-0.12.13}/tests/test_token_file_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|