python-aidot-cameras 0.7.24__tar.gz → 0.7.26__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.7.24/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.7.26}/PKG-INFO +4 -1
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/README.md +3 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/pyproject.toml +1 -1
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/client.py +82 -9
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26/src/python_aidot_cameras.egg-info}/PKG-INFO +4 -1
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/LICENSE +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/setup.cfg +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/__init__.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/aes_utils.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/__init__.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/constants.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/controls.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/models.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/playback.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/protocol.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/sdes.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/tutk.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/client.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/const.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/credentials.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/device_client.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/discover.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/exceptions.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/g711.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/login_const.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/__init__.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/device_client_model.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/device_model.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/discover_model.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/py.typed +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_token_refresh.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.26
|
|
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
|
|
@@ -122,6 +122,8 @@ chosen to work out of the box; override only when tuning.
|
|
|
122
122
|
| `AIDOT_SPROP_DIR` | Directory where captured SPS/PPS (sprop) parameter sets are cached. Set this to a writable path (e.g. for Home Assistant) if the default location is read-only. | `<package dir>` |
|
|
123
123
|
| `AIDOT_DISABLE_HIGHPORT_FIX` | If set (any value), disables the DTLS high-port `USE-CANDIDATE` nomination fix and falls back to upstream aioice behavior (used to measure the baseline connect rate). | unset (fix enabled) |
|
|
124
124
|
| `AIDOT_FAST_CONNECT` | Enables LAN-direct "fast connect" mode (STUN-only, skips several cloud signaling waits) when set to a truthy value. | unset (off) |
|
|
125
|
+
| `AIDOT_SDES_FAST_LIVEPLAY` | Truthy value skips the `livePlayResp` blocking wait for eligible SDES cameras (~4.5 s faster cold start), while keeping the full ICE/SCTP handshake. Role-reversal models (A001064 PTZ) are always excluded. Soak-validated; opt-in. | unset (off) |
|
|
126
|
+
| `AIDOT_SERVE_RELAY` | Holds the public stream port via an internal relay that proxies to ffmpeg, so the first (cold) view connects instead of failing while ffmpeg can't pre-bind the port. Set to `0` to serve ffmpeg directly. | `1` (enabled) |
|
|
125
127
|
| `AIDOT_MAX_CONCURRENT_OPENS` | Caps how many stream opens run concurrently. | `2` |
|
|
126
128
|
| `AIDOT_MAX_CONCURRENT_STREAMS` | Caps how many cameras stream at once. | `3` |
|
|
127
129
|
| `AIDOT_STREAM_IDLE_S` | Seconds of stream idle before an idle release. | `120` |
|
|
@@ -131,6 +133,7 @@ chosen to work out of the box; override only when tuning.
|
|
|
131
133
|
| `AIDOT_BUSY_RETRY_S` | Delay, in seconds, before retrying when a camera reports busy. | `45` |
|
|
132
134
|
| `AIDOT_LIVESTREAM_PARAM` | Set to `0` to skip the cloud `liveStreamParam` pre-connect that provisions battery cameras' live-stream sessions before signaling (without it, battery cameras like the L2 models reject streaming with `-50019`). | `1` (enabled) |
|
|
133
135
|
| `AIDOT_GOP_PLI_S` | Interval, in seconds, between PLI (keyframe) requests. | `2.0` |
|
|
136
|
+
| `AIDOT_SDES_PLI_GAPS` | Comma-separated second offsets for the early PLI (keyframe-request) burst on SDES cameras, to pull the first keyframe in faster on cold start. | `0,1.5,2,3` |
|
|
134
137
|
| `AIDOT_AUDIO_TARGET_DBFS` | Target loudness (dBFS) for two-way audio normalization. | `-15` |
|
|
135
138
|
| `AIDOT_AUDIO_MAXGAIN_DB` | Maximum gain (dB) applied by the audio normalizer. | `30` |
|
|
136
139
|
| `AIDOT_AUDIO_MINGAIN_DB` | Minimum gain (dB) applied by the audio normalizer. | `-12` |
|
|
@@ -95,6 +95,8 @@ chosen to work out of the box; override only when tuning.
|
|
|
95
95
|
| `AIDOT_SPROP_DIR` | Directory where captured SPS/PPS (sprop) parameter sets are cached. Set this to a writable path (e.g. for Home Assistant) if the default location is read-only. | `<package dir>` |
|
|
96
96
|
| `AIDOT_DISABLE_HIGHPORT_FIX` | If set (any value), disables the DTLS high-port `USE-CANDIDATE` nomination fix and falls back to upstream aioice behavior (used to measure the baseline connect rate). | unset (fix enabled) |
|
|
97
97
|
| `AIDOT_FAST_CONNECT` | Enables LAN-direct "fast connect" mode (STUN-only, skips several cloud signaling waits) when set to a truthy value. | unset (off) |
|
|
98
|
+
| `AIDOT_SDES_FAST_LIVEPLAY` | Truthy value skips the `livePlayResp` blocking wait for eligible SDES cameras (~4.5 s faster cold start), while keeping the full ICE/SCTP handshake. Role-reversal models (A001064 PTZ) are always excluded. Soak-validated; opt-in. | unset (off) |
|
|
99
|
+
| `AIDOT_SERVE_RELAY` | Holds the public stream port via an internal relay that proxies to ffmpeg, so the first (cold) view connects instead of failing while ffmpeg can't pre-bind the port. Set to `0` to serve ffmpeg directly. | `1` (enabled) |
|
|
98
100
|
| `AIDOT_MAX_CONCURRENT_OPENS` | Caps how many stream opens run concurrently. | `2` |
|
|
99
101
|
| `AIDOT_MAX_CONCURRENT_STREAMS` | Caps how many cameras stream at once. | `3` |
|
|
100
102
|
| `AIDOT_STREAM_IDLE_S` | Seconds of stream idle before an idle release. | `120` |
|
|
@@ -104,6 +106,7 @@ chosen to work out of the box; override only when tuning.
|
|
|
104
106
|
| `AIDOT_BUSY_RETRY_S` | Delay, in seconds, before retrying when a camera reports busy. | `45` |
|
|
105
107
|
| `AIDOT_LIVESTREAM_PARAM` | Set to `0` to skip the cloud `liveStreamParam` pre-connect that provisions battery cameras' live-stream sessions before signaling (without it, battery cameras like the L2 models reject streaming with `-50019`). | `1` (enabled) |
|
|
106
108
|
| `AIDOT_GOP_PLI_S` | Interval, in seconds, between PLI (keyframe) requests. | `2.0` |
|
|
109
|
+
| `AIDOT_SDES_PLI_GAPS` | Comma-separated second offsets for the early PLI (keyframe-request) burst on SDES cameras, to pull the first keyframe in faster on cold start. | `0,1.5,2,3` |
|
|
107
110
|
| `AIDOT_AUDIO_TARGET_DBFS` | Target loudness (dBFS) for two-way audio normalization. | `-15` |
|
|
108
111
|
| `AIDOT_AUDIO_MAXGAIN_DB` | Maximum gain (dB) applied by the audio normalizer. | `30` |
|
|
109
112
|
| `AIDOT_AUDIO_MINGAIN_DB` | Minimum gain (dB) applied by the audio normalizer. | `-12` |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.26"
|
|
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"
|
|
@@ -2559,6 +2559,30 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
2559
2559
|
return os.environ.get("AIDOT_SDES_FAST_LIVEPLAY", "").strip().lower() in (
|
|
2560
2560
|
"1", "true", "yes", "on")
|
|
2561
2561
|
|
|
2562
|
+
def _resolve_sdes_skip_turn(self) -> bool:
|
|
2563
|
+
"""EXPERIMENTAL (opt-in, default off): skip the blocking SDES TURN relay
|
|
2564
|
+
pre-allocation, for cameras reachable LAN-direct.
|
|
2565
|
+
|
|
2566
|
+
Before building the offer the SDES path does two synchronous RFC-5766
|
|
2567
|
+
Allocate round-trips (audio + video) to the cloud TURN server so the
|
|
2568
|
+
offer's c=/m= can carry a relay address - ~2-3 s of pure cold-start
|
|
2569
|
+
latency. On a LAN the camera's host candidate wins and that relay is
|
|
2570
|
+
never used, so skipping it shaves the latency - at the cost of no relay
|
|
2571
|
+
fallback for a camera on a different segment / behind strict NAT (the
|
|
2572
|
+
same trade-off ``AIDOT_FAST_CONNECT`` already makes for DTLS, and which
|
|
2573
|
+
is force-disabled for SDES because it *also* skips the SCTP-arming
|
|
2574
|
+
waits; this flag skips ONLY the relay pre-allocation, leaving the rest
|
|
2575
|
+
of the SDES handshake intact).
|
|
2576
|
+
|
|
2577
|
+
Per-camera ``sdes_skip_turn`` (set via start_keepalive) wins; else the
|
|
2578
|
+
``AIDOT_SDES_SKIP_TURN_PREALLOC`` env (truthy = 1/true/yes/on), default
|
|
2579
|
+
off. Only consulted for SDES cameras."""
|
|
2580
|
+
opt = getattr(self, "_sdes_skip_turn_opt", None)
|
|
2581
|
+
if opt is not None:
|
|
2582
|
+
return bool(opt)
|
|
2583
|
+
return os.environ.get("AIDOT_SDES_SKIP_TURN_PREALLOC", "").strip().lower() in (
|
|
2584
|
+
"1", "true", "yes", "on")
|
|
2585
|
+
|
|
2562
2586
|
def _maybe_start_serve_relay(self, serve_url: Optional[str]) -> "Optional[_ServeRelay]":
|
|
2563
2587
|
"""Hold the public serve port via a _ServeRelay so an eager go2rtc pull
|
|
2564
2588
|
connects-and-waits instead of hitting ECONNREFUSED during the ~16-25s
|
|
@@ -3720,7 +3744,14 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
3720
3744
|
or msg.get("devId") == device_id):
|
|
3721
3745
|
loop.call_soon_threadsafe(camera_ready_ev.set)
|
|
3722
3746
|
# livePlayResp: explicit camera ack/nack for start-play command.
|
|
3723
|
-
|
|
3747
|
+
# The camera echoes our peer_id (verified live); its payload has NO
|
|
3748
|
+
# devId, so the old devId-only match never fired and this future never
|
|
3749
|
+
# resolved (the wait always timed out). Match on the echoed peer_id
|
|
3750
|
+
# (per-open, precise); keep devId as a fallback for any camera that
|
|
3751
|
+
# does send it. dstAddr is the account-wide userId - too loose to use.
|
|
3752
|
+
if method == "livePlayResp" and (
|
|
3753
|
+
inner.get("peerid") == peer_id
|
|
3754
|
+
or inner.get("devId") == device_id):
|
|
3724
3755
|
if not liveplay_resp_fut.done():
|
|
3725
3756
|
loop.call_soon_threadsafe(liveplay_resp_fut.set_result, inner)
|
|
3726
3757
|
# livePlayReq echo: broker/camera confirmed delivery of our livePlayReq.
|
|
@@ -4186,9 +4217,19 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
4186
4217
|
_lp_arrived = True
|
|
4187
4218
|
_lp_code = int(_lp_resp.get("code", 200))
|
|
4188
4219
|
_lp_on = int(_lp_resp.get("livePlay", 1))
|
|
4189
|
-
|
|
4220
|
+
# Fast-fail only on an unambiguous refusal (livePlay=0).
|
|
4221
|
+
# Other non-OK codes (incl. -50019 "not ready") are
|
|
4222
|
+
# transient on battery cameras and recover via ICE; abort
|
|
4223
|
+
# there would spuriously kill otherwise-good streams.
|
|
4224
|
+
if _lp_on == 0:
|
|
4190
4225
|
raise RuntimeError(
|
|
4191
|
-
f"livePlay
|
|
4226
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code})"
|
|
4227
|
+
)
|
|
4228
|
+
elif _lp_code not in (0, 200):
|
|
4229
|
+
_status(
|
|
4230
|
+
f"livePlayResp: non-OK code {_lp_code}"
|
|
4231
|
+
f"{' (not ready, transient)' if _lp_code == -50019 else ''}"
|
|
4232
|
+
" - proceeding"
|
|
4192
4233
|
)
|
|
4193
4234
|
except TimeoutError:
|
|
4194
4235
|
pass
|
|
@@ -4231,9 +4272,15 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
4231
4272
|
_lp_resp2 = liveplay_resp_fut.result()
|
|
4232
4273
|
_lp_code2 = int(_lp_resp2.get("code", 200))
|
|
4233
4274
|
_lp_on2 = int(_lp_resp2.get("livePlay", 1))
|
|
4234
|
-
if
|
|
4275
|
+
if _lp_on2 == 0:
|
|
4235
4276
|
raise RuntimeError(
|
|
4236
|
-
f"livePlay
|
|
4277
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code2})"
|
|
4278
|
+
)
|
|
4279
|
+
elif _lp_code2 not in (0, 200):
|
|
4280
|
+
_status(
|
|
4281
|
+
f"livePlayResp: non-OK code {_lp_code2}"
|
|
4282
|
+
f"{' (not ready, transient)' if _lp_code2 == -50019 else ''}"
|
|
4283
|
+
" - proceeding"
|
|
4237
4284
|
)
|
|
4238
4285
|
except RuntimeError:
|
|
4239
4286
|
raise
|
|
@@ -6795,7 +6842,15 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
6795
6842
|
# AIDOT_FAST_CONNECT skips this blocking pre-allocation (LAN-direct mode):
|
|
6796
6843
|
# the offer goes out immediately with host/srflx candidates and the LAN
|
|
6797
6844
|
# path connects without waiting on a cloud TURN Allocate round-trip.
|
|
6798
|
-
|
|
6845
|
+
# AIDOT_SDES_SKIP_TURN_PREALLOC (experimental, opt-in) does the same skip
|
|
6846
|
+
# for SDES specifically, where _fast_connect is force-off (see
|
|
6847
|
+
# _resolve_sdes_skip_turn). Either way the cost is instrumented below so
|
|
6848
|
+
# the saving is measurable: grep ``signaling-wait[`` for sdes-turn-prealloc.
|
|
6849
|
+
_skip_turn_prealloc = self._resolve_sdes_skip_turn()
|
|
6850
|
+
_turn_t0 = time.monotonic()
|
|
6851
|
+
_turn_did = False
|
|
6852
|
+
if _sdes_turn_entries and not _fast_connect and not _skip_turn_prealloc:
|
|
6853
|
+
_turn_did = True
|
|
6799
6854
|
try:
|
|
6800
6855
|
import re as _re_pre
|
|
6801
6856
|
import hashlib as _hlk_pre
|
|
@@ -6832,6 +6887,16 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
6832
6887
|
)
|
|
6833
6888
|
except Exception as _pre_exc:
|
|
6834
6889
|
_LOGGER.warning("TURN pre-allocation error: %s", _pre_exc)
|
|
6890
|
+
if _skip_turn_prealloc and _sdes_turn_entries:
|
|
6891
|
+
_status(
|
|
6892
|
+
"AIDOT_SDES_SKIP_TURN_PREALLOC: skipping TURN relay"
|
|
6893
|
+
" pre-allocation (~2-3s) - host/srflx candidates only, LAN-direct"
|
|
6894
|
+
)
|
|
6895
|
+
_LOGGER.info(
|
|
6896
|
+
"signaling-wait[%s] sdes-turn-prealloc elapsed=%dms allocated=%d skipped=%s",
|
|
6897
|
+
self.device_id,
|
|
6898
|
+
int((time.monotonic() - _turn_t0) * 1000),
|
|
6899
|
+
len(_relay_addrs), bool(_skip_turn_prealloc))
|
|
6835
6900
|
|
|
6836
6901
|
# --- DTLS certificate for m=application probe ----------------------- #
|
|
6837
6902
|
# PreCon cameras (sptPreconn=1) need SESSION_MODE_REQ via SCTP datachannel.
|
|
@@ -7100,10 +7165,18 @@ class CameraMixin(_CameraControlsMixin):
|
|
|
7100
7165
|
_lp_arrived = True
|
|
7101
7166
|
_lp_code_sdes = int(_lp_resp_sdes.get("code", 200))
|
|
7102
7167
|
_lp_on_sdes = int(_lp_resp_sdes.get("livePlay", 1))
|
|
7103
|
-
|
|
7168
|
+
# Only an explicit livePlay=0 is an unambiguous refusal (fast-fail).
|
|
7169
|
+
# Numeric codes (e.g. -50019 "not ready" on a waking battery cam)
|
|
7170
|
+
# are transient - the camera recovers and streams - so log and
|
|
7171
|
+
# proceed rather than abort on a code we can't classify as terminal
|
|
7172
|
+
# (genuine terminal rejects are still caught on the webrtcResp ack).
|
|
7173
|
+
if _lp_on_sdes == 0:
|
|
7104
7174
|
raise RuntimeError(
|
|
7105
|
-
f"livePlay
|
|
7106
|
-
|
|
7175
|
+
f"livePlay refused by camera (livePlay=0, code={_lp_code_sdes})")
|
|
7176
|
+
elif _lp_code_sdes not in (0, 200):
|
|
7177
|
+
_status(f"livePlayResp: non-OK code {_lp_code_sdes}"
|
|
7178
|
+
f"{' (not ready, transient)' if _lp_code_sdes == -50019 else ''}"
|
|
7179
|
+
" - proceeding")
|
|
7107
7180
|
except TimeoutError:
|
|
7108
7181
|
pass
|
|
7109
7182
|
_LOGGER.info(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.26
|
|
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
|
|
@@ -122,6 +122,8 @@ chosen to work out of the box; override only when tuning.
|
|
|
122
122
|
| `AIDOT_SPROP_DIR` | Directory where captured SPS/PPS (sprop) parameter sets are cached. Set this to a writable path (e.g. for Home Assistant) if the default location is read-only. | `<package dir>` |
|
|
123
123
|
| `AIDOT_DISABLE_HIGHPORT_FIX` | If set (any value), disables the DTLS high-port `USE-CANDIDATE` nomination fix and falls back to upstream aioice behavior (used to measure the baseline connect rate). | unset (fix enabled) |
|
|
124
124
|
| `AIDOT_FAST_CONNECT` | Enables LAN-direct "fast connect" mode (STUN-only, skips several cloud signaling waits) when set to a truthy value. | unset (off) |
|
|
125
|
+
| `AIDOT_SDES_FAST_LIVEPLAY` | Truthy value skips the `livePlayResp` blocking wait for eligible SDES cameras (~4.5 s faster cold start), while keeping the full ICE/SCTP handshake. Role-reversal models (A001064 PTZ) are always excluded. Soak-validated; opt-in. | unset (off) |
|
|
126
|
+
| `AIDOT_SERVE_RELAY` | Holds the public stream port via an internal relay that proxies to ffmpeg, so the first (cold) view connects instead of failing while ffmpeg can't pre-bind the port. Set to `0` to serve ffmpeg directly. | `1` (enabled) |
|
|
125
127
|
| `AIDOT_MAX_CONCURRENT_OPENS` | Caps how many stream opens run concurrently. | `2` |
|
|
126
128
|
| `AIDOT_MAX_CONCURRENT_STREAMS` | Caps how many cameras stream at once. | `3` |
|
|
127
129
|
| `AIDOT_STREAM_IDLE_S` | Seconds of stream idle before an idle release. | `120` |
|
|
@@ -131,6 +133,7 @@ chosen to work out of the box; override only when tuning.
|
|
|
131
133
|
| `AIDOT_BUSY_RETRY_S` | Delay, in seconds, before retrying when a camera reports busy. | `45` |
|
|
132
134
|
| `AIDOT_LIVESTREAM_PARAM` | Set to `0` to skip the cloud `liveStreamParam` pre-connect that provisions battery cameras' live-stream sessions before signaling (without it, battery cameras like the L2 models reject streaming with `-50019`). | `1` (enabled) |
|
|
133
135
|
| `AIDOT_GOP_PLI_S` | Interval, in seconds, between PLI (keyframe) requests. | `2.0` |
|
|
136
|
+
| `AIDOT_SDES_PLI_GAPS` | Comma-separated second offsets for the early PLI (keyframe-request) burst on SDES cameras, to pull the first keyframe in faster on cold start. | `0,1.5,2,3` |
|
|
134
137
|
| `AIDOT_AUDIO_TARGET_DBFS` | Target loudness (dBFS) for two-way audio normalization. | `-15` |
|
|
135
138
|
| `AIDOT_AUDIO_MAXGAIN_DB` | Maximum gain (dB) applied by the audio normalizer. | `30` |
|
|
136
139
|
| `AIDOT_AUDIO_MINGAIN_DB` | Minimum gain (dB) applied by the audio normalizer. | `-12` |
|
|
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
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/device_client_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/device_model.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/src/aidot/models/discover_model.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
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.7.24 → python_aidot_cameras-0.7.26}/tests/test_sdes_fast_liveplay.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
|