python-aidot-cameras 0.9.1__tar.gz → 0.9.2__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 (71) hide show
  1. {python_aidot_cameras-0.9.1/src/python_aidot_cameras.egg-info → python_aidot_cameras-0.9.2}/PKG-INFO +1 -1
  2. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/pyproject.toml +1 -1
  3. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/protocol.py +31 -0
  4. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2/src/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/LICENSE +0 -0
  6. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/MANIFEST.in +0 -0
  7. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/README.md +0 -0
  8. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/setup.cfg +0 -0
  9. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/__init__.py +0 -0
  10. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/aes_utils.py +0 -0
  11. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/__init__.py +0 -0
  12. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/client.py +0 -0
  13. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/constants.py +0 -0
  14. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/controls.py +0 -0
  15. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/go2rtc.py +0 -0
  16. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/lan_control.py +0 -0
  17. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/models.py +0 -0
  18. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/playback.py +0 -0
  19. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/sdes.py +0 -0
  20. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/sdes_open.py +0 -0
  21. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/tutk.py +0 -0
  22. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/webrtc.py +0 -0
  23. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/camera/webrtc_open.py +0 -0
  24. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/client.py +0 -0
  25. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/const.py +0 -0
  26. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/credentials.py +0 -0
  27. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/device_client.py +0 -0
  28. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/discover.py +0 -0
  29. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/exceptions.py +0 -0
  30. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/g711.py +0 -0
  31. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/login_const.py +0 -0
  32. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/models/__init__.py +0 -0
  33. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/models/device_client_model.py +0 -0
  34. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/models/device_model.py +0 -0
  35. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/models/discover_model.py +0 -0
  36. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/aidot/py.typed +0 -0
  37. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
  38. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  39. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/python_aidot_cameras.egg-info/requires.txt +0 -0
  40. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/src/python_aidot_cameras.egg-info/top_level.txt +0 -0
  41. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_aioice_compat.py +0 -0
  42. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_alarm_event.py +0 -0
  43. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_backoff.py +0 -0
  44. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_device_login_guard.py +0 -0
  45. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_go2rtc.py +0 -0
  46. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_highport_nomination.py +0 -0
  47. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_ice_config_cache.py +0 -0
  48. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_lan_control.py +0 -0
  49. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_live_stream_param.py +0 -0
  50. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_motion_poll.py +0 -0
  51. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_no_undefined_names.py +0 -0
  52. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_persistent_mqtt.py +0 -0
  53. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_persistent_mqtt_robustness.py +0 -0
  54. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_post_merge_hardening.py +0 -0
  55. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_adaptive.py +0 -0
  56. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_fast_liveplay.py +0 -0
  57. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_idle_release.py +0 -0
  58. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_serve_audio.py +0 -0
  59. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_serve_cmd.py +0 -0
  60. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_sprop.py +0 -0
  61. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_talk.py +0 -0
  62. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_sdes_watchdog.py +0 -0
  63. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_serve_relay.py +0 -0
  64. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_session_stats.py +0 -0
  65. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_speak.py +0 -0
  66. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_stream_cap.py +0 -0
  67. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_stream_idle.py +0 -0
  68. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_stream_teardown.py +0 -0
  69. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_talk.py +0 -0
  70. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/tests/test_terminal_ack.py +0 -0
  71. {python_aidot_cameras-0.9.1 → python_aidot_cameras-0.9.2}/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.9.1
3
+ Version: 0.9.2
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.9.1"
7
+ version = "0.9.2"
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"
@@ -897,6 +897,8 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
897
897
  have_audio = False
898
898
  v0 = [None]
899
899
  a_pts = [0]
900
+ a_rtp0 = [None] # first audio RTP timestamp (8 kHz units), for gap detection
901
+ a_in = [0] # 8 kHz samples emitted to the resampler so far (incl. concealed)
900
902
  vstarted = [False]
901
903
 
902
904
  def _flush_video():
@@ -934,7 +936,35 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
934
936
  data, _ts = aq.get_nowait()
935
937
  except _q.Empty:
936
938
  break
939
+ # Lock audio to its RTP clock. The camera's PCMA RTP timestamp is
940
+ # an 8 kHz sample count; on packet loss it jumps ahead of the
941
+ # samples we've actually decoded. Without filling that gap the
942
+ # audio timeline silently COMPRESSES (the lost time vanishes), so
943
+ # audio runs ahead of the RTP-timestamped video -> growing A/V
944
+ # desync and player jitter-buffer resyncs, heard as choppiness.
945
+ # Conceal the gap with silence (PLC) through the same stateful
946
+ # resampler, mirroring what the video path does with its 90 kHz
947
+ # timestamps. Lossless streams have _gap == 0 (no-op).
948
+ if _np is not None:
949
+ if a_rtp0[0] is None:
950
+ a_rtp0[0] = int(_ts)
951
+ _expected = (int(_ts) - a_rtp0[0]) & 0xFFFFFFFF
952
+ _gap = _expected - a_in[0]
953
+ if _gap < 0 or _gap > 8000 * 5:
954
+ a_in[0] = _expected # wrap / stream reset -> resync, no fill
955
+ elif _gap >= 160: # >= 20 ms missing: conceal with silence
956
+ try:
957
+ _sil = _np.zeros((1, int(_gap)), dtype=_np.int16)
958
+ _sfr = av.AudioFrame.from_ndarray(
959
+ _sil, format="s16", layout="mono")
960
+ _sfr.sample_rate = 8000
961
+ for _rfr in resampler.resample(_sfr):
962
+ fifo.write(_rfr)
963
+ a_in[0] += int(_gap)
964
+ except Exception:
965
+ _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
937
966
  for fr in adec.decode(av.Packet(data)):
967
+ _ndec = fr.samples
938
968
  fr.pts = None
939
969
  if _np is not None:
940
970
  try:
@@ -964,6 +994,7 @@ def _dtls_av_mux_run(vq, aq, out_fileobj, progress, stop_flag) -> None:
964
994
  _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
965
995
  for rfr in resampler.resample(fr): # 8k PCMA -> 48k fltp
966
996
  fifo.write(rfr)
997
+ a_in[0] += _ndec
967
998
  except Exception:
968
999
  _LOGGER.debug("swallowed exception in %s", '_flush_audio', exc_info=True)
969
1000
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.9.1
3
+ Version: 0.9.2
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