python-aidot-cameras 0.7.2__tar.gz → 0.7.5__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.
Files changed (42) hide show
  1. {python_aidot_cameras-0.7.2/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.7.5}/PKG-INFO +1 -1
  2. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/pyproject.toml +1 -1
  3. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/camera/client.py +28 -2
  4. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5/src/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/LICENSE +0 -0
  6. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/README.md +0 -0
  7. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/setup.cfg +0 -0
  8. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/__init__.py +0 -0
  9. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/aes_utils.py +0 -0
  10. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/camera/__init__.py +0 -0
  11. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/camera/lan_control.py +0 -0
  12. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/client.py +0 -0
  13. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/const.py +0 -0
  14. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/credentials.py +0 -0
  15. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/device_client.py +0 -0
  16. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/discover.py +0 -0
  17. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/exceptions.py +0 -0
  18. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/g711.py +0 -0
  19. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/login_const.py +0 -0
  20. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/models/__init__.py +0 -0
  21. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/models/device_client_model.py +0 -0
  22. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/models/device_model.py +0 -0
  23. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/aidot/models/discover_model.py +0 -0
  24. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
  25. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  26. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
  27. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
  28. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_alarm_event.py +0 -0
  29. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_highport_nomination.py +0 -0
  30. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_lan_control.py +0 -0
  31. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_motion_poll.py +0 -0
  32. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_no_undefined_names.py +0 -0
  33. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_post_merge_hardening.py +0 -0
  34. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_sdes_idle_release.py +0 -0
  35. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_sdes_sprop.py +0 -0
  36. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_sdes_talk.py +0 -0
  37. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_sdes_watchdog.py +0 -0
  38. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_speak.py +0 -0
  39. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_stream_cap.py +0 -0
  40. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_talk.py +0 -0
  41. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/tests/test_terminal_ack.py +0 -0
  42. {python_aidot_cameras-0.7.2 → python_aidot_cameras-0.7.5}/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.2
3
+ Version: 0.7.5
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "0.7.2"
7
+ version = "0.7.5"
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"
@@ -2168,8 +2168,15 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
2168
2168
  _target = _db2amp(float(os.environ.get("AIDOT_AUDIO_TARGET_DBFS", "-15"))) * 32767.0
2169
2169
  _maxg = _db2amp(float(os.environ.get("AIDOT_AUDIO_MAXGAIN_DB", "30")))
2170
2170
  _ming = _db2amp(float(os.environ.get("AIDOT_AUDIO_MINGAIN_DB", "-12")))
2171
+ # Noise gate: below this RMS the input is (near) silence, so the AGC
2172
+ # must NOT crank toward +maxg - doing so amplifies the A-law
2173
+ # quantization floor into audible high-frequency clicking on a quiet
2174
+ # camera. Gain is scaled down quadratically below the gate. Set very
2175
+ # low (e.g. -120) to disable.
2176
+ _gate = _db2amp(float(os.environ.get("AIDOT_AUDIO_GATE_DBFS", "-45"))) * 32767.0
2171
2177
  except (ValueError, TypeError):
2172
2178
  _target, _maxg, _ming = _db2amp(-15) * 32767.0, _db2amp(30), _db2amp(-12)
2179
+ _gate = _db2amp(-45) * 32767.0
2173
2180
  _agc_ms = [None] # smoothed mean-square (level tracker)
2174
2181
  try:
2175
2182
  import numpy as _np
@@ -2232,6 +2239,12 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
2232
2239
  _agc_ms[0] = _agc_ms[0] * 0.95 + _ms * 0.05
2233
2240
  _rms = (_agc_ms[0] ** 0.5) + 1.0
2234
2241
  _gain = min(_maxg, max(_ming, _target / _rms))
2242
+ # Noise gate: when the level is below the gate floor the
2243
+ # signal is essentially silence; scale the gain down
2244
+ # quadratically so the A-law quantization noise is not
2245
+ # amplified into audible clicking.
2246
+ if _rms < _gate:
2247
+ _gain *= (_rms / _gate) ** 2
2235
2248
  # tanh soft-limiter catches transient peaks past target.
2236
2249
  _y = _np.tanh(_x * (_gain / 32767.0)) * 32767.0
2237
2250
  _g = av.AudioFrame.from_ndarray(
@@ -4821,7 +4834,13 @@ class CameraMixin:
4821
4834
  await asyncio.sleep(0.2)
4822
4835
  if progress[0] > _p0:
4823
4836
  break
4824
- await asyncio.sleep(2.0) # let ffmpeg analyze input + bind
4837
+ # ffmpeg binds its -listen socket at startup, so by the time
4838
+ # the mux has written a frame (progress moved above) the
4839
+ # socket is long bound. A short margin is plenty; the old
4840
+ # 2.0s blind wait was pure cold-start latency. (Don't probe
4841
+ # the port - `-listen 1` would consume our probe as its one
4842
+ # client and tear down before go2rtc connects.)
4843
+ await asyncio.sleep(0.3)
4825
4844
  self._serve_ready.set()
4826
4845
  # Wait for ffmpeg to exit (go2rtc disconnect) or idle release.
4827
4846
  while self._streaming_active and proc.returncode is None:
@@ -11994,7 +12013,14 @@ class CameraMixin:
11994
12013
  _sdes_gain_db = -8.0
11995
12014
  dest_args = [
11996
12015
  "-c:v", "copy",
11997
- "-af", f"volume={_sdes_gain_db}dB",
12016
+ # aresample=async=1 fills timing gaps with silence so the AAC
12017
+ # encoder always has a full 1024-sample frame to emit. Battery
12018
+ # cameras send PCMA sparsely (radio duty-cycling + weak uplink);
12019
+ # without this the encoder starves, the mpegts PAT/PMT is never
12020
+ # written, and the consumer gets ZERO bytes - the whole stream
12021
+ # dies. first_pts=0 anchors the clock so the first AAC frame
12022
+ # (and thus the PMT) is produced promptly at stream start.
12023
+ "-af", f"aresample=async=1:first_pts=0,volume={_sdes_gain_db}dB",
11998
12024
  "-c:a", "aac", "-ar", "48000", "-b:a", "128k",
11999
12025
  "-f", "mpegts", "-listen", "1",
12000
12026
  rtsp_push_url,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.7.2
3
+ Version: 0.7.5
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