python-aidot-cameras 0.15.5__tar.gz → 0.15.7__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 (177) hide show
  1. {python_aidot_cameras-0.15.5/python_aidot_cameras.egg-info → python_aidot_cameras-0.15.7}/PKG-INFO +1 -1
  2. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/sdes_open.py +47 -1
  3. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/pyproject.toml +1 -1
  4. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/python_aidot_cameras.egg-info/SOURCES.txt +2 -0
  6. python_aidot_cameras-0.15.7/tests/test_bridge_state_sentinels.py +68 -0
  7. python_aidot_cameras-0.15.7/tests/test_media_socket_rcvbuf.py +69 -0
  8. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/LICENSE +0 -0
  9. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/MANIFEST.in +0 -0
  10. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/README.md +0 -0
  11. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/__init__.py +0 -0
  12. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/__main__.py +0 -0
  13. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_upstream.py +0 -0
  14. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/__init__.py +0 -0
  15. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  16. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  17. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  18. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  19. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  20. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  21. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  22. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  23. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  24. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  25. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  26. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  27. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  28. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  29. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  30. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  31. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  32. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  33. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  34. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  35. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  36. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  37. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  38. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  39. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  40. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  41. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  42. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  43. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  44. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  45. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  46. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  47. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/ble_gateway.py +0 -0
  48. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/__init__.py +0 -0
  49. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/client.py +0 -0
  50. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/constants.py +0 -0
  51. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/controls.py +0 -0
  52. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/go2rtc.py +0 -0
  53. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/hwaccel.py +0 -0
  54. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/lan_control.py +0 -0
  55. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/models.py +0 -0
  56. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/playback.py +0 -0
  57. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/protocol.py +0 -0
  58. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/sdes.py +0 -0
  59. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/tutk.py +0 -0
  60. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/webrtc.py +0 -0
  61. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/camera/webrtc_open.py +0 -0
  62. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/client.py +0 -0
  63. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/const.py +0 -0
  64. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/credentials.py +0 -0
  65. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/crypto.py +0 -0
  66. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/device_client.py +0 -0
  67. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/discover.py +0 -0
  68. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/exceptions.py +0 -0
  69. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/g711.py +0 -0
  70. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/aidot_cameras/py.typed +0 -0
  71. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  72. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  73. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/python_aidot_cameras.egg-info/requires.txt +0 -0
  74. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  75. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/setup.cfg +0 -0
  76. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_aioice_compat.py +0 -0
  77. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_alarm_event.py +0 -0
  78. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_answer_sdp_video_pt.py +0 -0
  79. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_audio_mux_offline.py +0 -0
  80. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_backoff.py +0 -0
  81. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_battery_detection.py +0 -0
  82. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_battery_evidence_corroboration.py +0 -0
  83. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_battery_skip_turn.py +0 -0
  84. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_ble_gateway.py +0 -0
  85. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_constants_imports_resolve.py +0 -0
  86. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_deferred_hardening.py +0 -0
  87. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_device_login_guard.py +0 -0
  88. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_device_user_info_cache.py +0 -0
  89. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dormant_camera_cleanup.py +0 -0
  90. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_mux_terminal_write.py +0 -0
  91. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_not_ready_burst.py +0 -0
  92. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_pinning.py +0 -0
  93. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_serve_decode_skip.py +0 -0
  94. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_serve_open_timeout.py +0 -0
  95. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_skip_signaling_wait.py +0 -0
  96. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_slow_probe.py +0 -0
  97. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_dtls_stdout_producer.py +0 -0
  98. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_egress_guard.py +0 -0
  99. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_env_seams.py +0 -0
  100. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_go2rtc.py +0 -0
  101. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_go2rtc_cli.py +0 -0
  102. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_h264_decode_rate_limit.py +0 -0
  103. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_highport_nomination.py +0 -0
  104. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_hwaccel_probe.py +0 -0
  105. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_ice_config_cache.py +0 -0
  106. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_ice_peer_reflexive.py +0 -0
  107. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_idle_release_both_loops.py +0 -0
  108. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_keepalive_renew.py +0 -0
  109. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_key_restart_sdp.py +0 -0
  110. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_keyframe_prompter.py +0 -0
  111. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_lan_control.py +0 -0
  112. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_light_active_color_mode.py +0 -0
  113. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_live_stream_param.py +0 -0
  114. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_live_validate_policy.py +0 -0
  115. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_logging_caps.py +0 -0
  116. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_media_wait_floor.py +0 -0
  117. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_motion_poll.py +0 -0
  118. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_mqtt_credential_refetch.py +0 -0
  119. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_mqtt_credential_self_heal.py +0 -0
  120. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_mqtt_password_not_persisted.py +0 -0
  121. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_mqtt_session_reconnect.py +0 -0
  122. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_narrow_pc_ice.py +0 -0
  123. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_no_self_referential_go2rtc.py +0 -0
  124. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_no_undefined_names.py +0 -0
  125. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_offline_keepalive_pause.py +0 -0
  126. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_open_fail_logger.py +0 -0
  127. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_open_gate_delay.py +0 -0
  128. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_peerid_reuse.py +0 -0
  129. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_persistent_mqtt.py +0 -0
  130. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_persistent_mqtt_robustness.py +0 -0
  131. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_playback_tls.py +0 -0
  132. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_post_merge_hardening.py +0 -0
  133. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_resolution_persists.py +0 -0
  134. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_restart_cycle.py +0 -0
  135. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_retry_policy.py +0 -0
  136. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_scripts_importable.py +0 -0
  137. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_adaptive.py +0 -0
  138. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_bridge_break_gate.py +0 -0
  139. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_echo_wait_timeout.py +0 -0
  140. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_fast_liveplay.py +0 -0
  141. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  142. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_idle_release.py +0 -0
  143. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_open_cleanup.py +0 -0
  144. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_serve_audio.py +0 -0
  145. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_serve_cmd.py +0 -0
  146. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_serve_stderr_drain.py +0 -0
  147. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_sprop.py +0 -0
  148. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_talk.py +0 -0
  149. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_teardown_exit_log.py +0 -0
  150. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_terminal_ack.py +0 -0
  151. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_terminal_ack_latency.py +0 -0
  152. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdes_watchdog.py +0 -0
  153. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdp_narrowing_properties.py +0 -0
  154. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sdp_payload_type_narrowing.py +0 -0
  155. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_security_hardening.py +0 -0
  156. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_serializable_login_info.py +0 -0
  157. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_serve_relay.py +0 -0
  158. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_session_stats.py +0 -0
  159. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_shared_house_seam.py +0 -0
  160. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_speak.py +0 -0
  161. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_sprop_staleness.py +0 -0
  162. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_stream_cap.py +0 -0
  163. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_stream_drain_ownership.py +0 -0
  164. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_stream_idle.py +0 -0
  165. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_stream_slot_cap.py +0 -0
  166. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_stream_teardown.py +0 -0
  167. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_talk.py +0 -0
  168. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_terminal_ack.py +0 -0
  169. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_token_file_robustness.py +0 -0
  170. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_token_refresh.py +0 -0
  171. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_turn_create_permission.py +0 -0
  172. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_upstream_compat.py +0 -0
  173. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_viewer_check_throttle.py +0 -0
  174. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_viewer_presence.py +0 -0
  175. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_viewer_present_push_mode.py +0 -0
  176. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_wait_or_event.py +0 -0
  177. {python_aidot_cameras-0.15.5 → python_aidot_cameras-0.15.7}/tests/test_wake_readiness_retry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.15.5
3
+ Version: 0.15.7
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
@@ -39,6 +39,41 @@ from .protocol import (
39
39
  _LOGGER = logging.getLogger(__name__)
40
40
 
41
41
 
42
+ #: What we ask the kernel for on a media receive socket. The kernel clamps to
43
+ #: ``net.core.rmem_max`` (4 MB on Home Assistant OS), and doubles what it grants
44
+ #: for bookkeeping, so asking for more than the cap is harmless.
45
+ _MEDIA_RCVBUF_BYTES = 4 * 1024 * 1024
46
+
47
+
48
+ def _widen_media_rcvbuf(sock, kind: str, device_id: str = "?") -> int:
49
+ """Ask for a large receive buffer on a camera media socket.
50
+
51
+ These sockets ran on the OS default, which is 208 KB on Home Assistant OS.
52
+ A keyframe from an A001064 is 146-190 KB and arrives as one burst of ~130
53
+ packets, so a single keyframe nearly fills that buffer: any delay in the
54
+ reader - the GIL while another camera's bridge runs, an ffmpeg write
55
+ blocking - and the kernel drops the tail of the burst. ffmpeg then reports
56
+ ``RTP: missed N packets`` and backs up its input queue.
57
+
58
+ Returns the buffer the kernel actually granted (it reports double what it
59
+ reserves), or 0 if the option could not be set. Best-effort by design: a
60
+ platform that refuses the option must not stop a camera from streaming.
61
+ """
62
+ try:
63
+ import socket as _sock_rb
64
+
65
+ sock.setsockopt(_sock_rb.SOL_SOCKET, _sock_rb.SO_RCVBUF,
66
+ _MEDIA_RCVBUF_BYTES)
67
+ got = sock.getsockopt(_sock_rb.SOL_SOCKET, _sock_rb.SO_RCVBUF)
68
+ except Exception:
69
+ _LOGGER.debug("camera %s: could not widen the %s receive buffer",
70
+ device_id, kind, exc_info=True)
71
+ return 0
72
+ _LOGGER.debug("camera %s: %s receive buffer %d bytes (asked %d)",
73
+ device_id, kind, got, _MEDIA_RCVBUF_BYTES)
74
+ return got
75
+
76
+
42
77
  def _start_serve_stderr_drain(proc, *, maxlines: int = 40) -> None:
43
78
  """Drain a serve ffmpeg's stderr continuously into a bounded tail on the proc.
44
79
 
@@ -529,11 +564,13 @@ class _SdesOpenMixin:
529
564
  import socket as _socket
530
565
 
531
566
  _audio_sock = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM)
567
+ _widen_media_rcvbuf(_audio_sock, "audio", getattr(self, "device_id", "?"))
532
568
  _audio_sock.bind(("0.0.0.0", 0))
533
569
  audio_port = _audio_sock.getsockname()[1]
534
570
  _cl(_audio_sock.close) # also unblocks the bridge thread's recv on cleanup
535
571
 
536
572
  _video_sock = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM)
573
+ _widen_media_rcvbuf(_video_sock, "video", getattr(self, "device_id", "?"))
537
574
  _video_sock.bind(("0.0.0.0", 0))
538
575
  video_port = _video_sock.getsockname()[1]
539
576
  _cl(_video_sock.close)
@@ -3600,7 +3637,16 @@ class _SdesOpenMixin:
3600
3637
  if not _sdes_probe_received:
3601
3638
  _sdes_probe_received = True
3602
3639
  _last_hb_ts = _time_br.time() # HB fires in 10s
3603
- if not hasattr(_bridge_fn, '_tutk_seq'):
3640
+ # Guard on a counter that is actually assigned.
3641
+ # This used to test `_tutk_seq`, a name nothing
3642
+ # ever set, so hasattr was False on EVERY packet
3643
+ # and both counters were reset to 0 each time -
3644
+ # every synthesized packet went out with
3645
+ # sequence number 1. ffmpeg reads a constant
3646
+ # sequence as a stream of discontinuities and
3647
+ # reports `RTP: missed N packets` for loss that
3648
+ # never happened.
3649
+ if not hasattr(_bridge_fn, '_tutk_seq_v'):
3604
3650
  _bridge_fn._tutk_seq_a = 0
3605
3651
  _bridge_fn._tutk_seq_v = 0
3606
3652
  if _tk_audio:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "0.15.5"
7
+ version = "0.15.7"
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 0.15.5
3
+ Version: 0.15.7
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
@@ -78,6 +78,7 @@ tests/test_battery_detection.py
78
78
  tests/test_battery_evidence_corroboration.py
79
79
  tests/test_battery_skip_turn.py
80
80
  tests/test_ble_gateway.py
81
+ tests/test_bridge_state_sentinels.py
81
82
  tests/test_constants_imports_resolve.py
82
83
  tests/test_deferred_hardening.py
83
84
  tests/test_device_login_guard.py
@@ -109,6 +110,7 @@ tests/test_light_active_color_mode.py
109
110
  tests/test_live_stream_param.py
110
111
  tests/test_live_validate_policy.py
111
112
  tests/test_logging_caps.py
113
+ tests/test_media_socket_rcvbuf.py
112
114
  tests/test_media_wait_floor.py
113
115
  tests/test_motion_poll.py
114
116
  tests/test_mqtt_credential_refetch.py
@@ -0,0 +1,68 @@
1
+ """Lazy-init guards on the bridge must name a variable something actually sets.
2
+
3
+ The bridge keeps its per-session state as attributes on its own function object
4
+ and initialises them with `if not hasattr(_bridge_fn, 'X')`. That idiom fails
5
+ silently when `X` is not the attribute the block goes on to assign: the guard is
6
+ true on every packet, so the state resets every packet, and nothing raises.
7
+
8
+ It happened. The TUTK-to-RTP synthesis guarded on `_tutk_seq` while assigning
9
+ `_tutk_seq_a` and `_tutk_seq_v`, so both sequence counters reset on every packet
10
+ and every synthesized RTP packet carried sequence number 1. ffmpeg reads a
11
+ constant sequence as a stream of discontinuities and logs `RTP: missed N
12
+ packets` - loss that never happened, on a hop where the kernel counters showed
13
+ zero drops. Measured on the box: 0 drops on every socket in the path while
14
+ ffmpeg reported 21 new missed-packet lines in one window.
15
+
16
+ These tests read the source because the bridge is one long closure that cannot
17
+ be called in isolation.
18
+ """
19
+ import inspect
20
+ import re
21
+
22
+ from aidot_cameras.camera import sdes_open
23
+
24
+ _SRC = inspect.getsource(sdes_open)
25
+
26
+
27
+ def _assigned_attrs() -> set:
28
+ """Every `_bridge_fn.X = ...` attribute assigned anywhere in the module."""
29
+ return set(re.findall(r"_bridge_fn\.([A-Za-z_][A-Za-z0-9_]*)\s*=[^=]", _SRC))
30
+
31
+
32
+ def test_every_hasattr_sentinel_is_a_name_something_assigns():
33
+ guarded = set(re.findall(r"hasattr\(_bridge_fn,\s*'([^']+)'\)", _SRC))
34
+ assigned = _assigned_attrs()
35
+ orphans = sorted(guarded - assigned)
36
+ assert not orphans, (
37
+ "these hasattr guards name attributes nothing ever assigns, so they are "
38
+ f"true on every packet and reset their state each time: {orphans}"
39
+ )
40
+
41
+
42
+ def test_the_tutk_sequence_counters_survive_between_packets():
43
+ """The specific regression: a constant RTP sequence number.
44
+
45
+ Pin the guard to a counter the block assigns, so a reset-per-packet cannot
46
+ come back by renaming one side of the pair.
47
+ """
48
+ block = re.search(
49
+ r"if not hasattr\(_bridge_fn,\s*'([^']+)'\):\s*\n"
50
+ r"\s*_bridge_fn\._tutk_seq_a = 0\s*\n"
51
+ r"\s*_bridge_fn\._tutk_seq_v = 0",
52
+ _SRC,
53
+ )
54
+ assert block, "the TUTK sequence init block moved - re-point this test"
55
+ sentinel = block.group(1)
56
+ assert sentinel in ("_tutk_seq_a", "_tutk_seq_v"), (
57
+ f"the guard tests {sentinel!r}, which is not one of the counters it "
58
+ "initialises - the counters will reset on every packet and every "
59
+ "synthesized packet will carry the same sequence number"
60
+ )
61
+
62
+
63
+ def test_the_counters_increment_rather_than_being_reassigned():
64
+ """Both counters must advance from their previous value, not restart."""
65
+ for name in ("_tutk_seq_a", "_tutk_seq_v"):
66
+ assert re.search(
67
+ rf"_bridge_fn\.{name} = \(_bridge_fn\.{name} \+ 1\) & 0xFFFF", _SRC
68
+ ), f"{name} no longer advances from its own previous value"
@@ -0,0 +1,69 @@
1
+ """Camera media sockets must ask for a receive buffer big enough for a keyframe.
2
+
3
+ These sockets ran on the OS default - 208 KB on Home Assistant OS. An A001064
4
+ keyframe is 146-190 KB delivered as one burst of ~130 packets, so a single
5
+ keyframe nearly fills that buffer. Any delay in the reader (the GIL while
6
+ another camera's bridge runs, an ffmpeg write blocking) and the kernel drops the
7
+ tail of the burst, which ffmpeg reports as ``RTP: missed N packets`` while its
8
+ input queue backs up. The box carried 44.3 million ``RcvbufErrors`` against
9
+ 161 million datagrams received over four days, and every UDP receive error on it
10
+ was a buffer overflow rather than a checksum or port error.
11
+ """
12
+ import socket
13
+
14
+ from aidot_cameras.camera.sdes_open import (
15
+ _MEDIA_RCVBUF_BYTES,
16
+ _widen_media_rcvbuf,
17
+ )
18
+
19
+
20
+ def test_the_request_clears_a_keyframe_burst():
21
+ # 190 KB is the largest keyframe measured on the A001064; the request has to
22
+ # leave room for that plus scheduling jitter, not merely match it.
23
+ assert _MEDIA_RCVBUF_BYTES >= 8 * 190 * 1024
24
+
25
+
26
+ def test_a_real_socket_gets_more_than_the_os_default():
27
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
28
+ try:
29
+ default = sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
30
+ got = _widen_media_rcvbuf(sock, "video", "dev-abc")
31
+ assert got > 0, "the option was not set at all"
32
+ # Kernels clamp to rmem_max and report double what they reserve, so the
33
+ # exact number is not assertable - only that it grew, and that it now
34
+ # clears one keyframe.
35
+ assert got >= default
36
+ assert got >= 190 * 1024, f"granted {got} bytes, under one keyframe"
37
+ finally:
38
+ sock.close()
39
+
40
+
41
+ def test_failure_is_survivable():
42
+ """A platform that refuses the option must not stop a camera streaming."""
43
+
44
+ class _Refuses:
45
+ def setsockopt(self, *_a):
46
+ raise OSError("nope")
47
+
48
+ def getsockopt(self, *_a):
49
+ raise OSError("nope")
50
+
51
+ assert _widen_media_rcvbuf(_Refuses(), "audio", "dev-abc") == 0
52
+
53
+
54
+ def test_both_media_sockets_are_widened_before_bind():
55
+ """Pin the call sites: a socket widened after bind may already have dropped.
56
+
57
+ The open path is one long method that cannot be called in isolation, so
58
+ assert on its source - both the audio and video sockets must be widened, and
59
+ each before its own bind.
60
+ """
61
+ import inspect
62
+
63
+ from aidot_cameras.camera import sdes_open
64
+
65
+ src = inspect.getsource(sdes_open)
66
+ for kind in ("_audio_sock", "_video_sock"):
67
+ widen = src.index(f"_widen_media_rcvbuf({kind}")
68
+ bind = src.index(f"{kind}.bind(")
69
+ assert widen < bind, f"{kind} is bound before its receive buffer is set"