python-aidot-cameras 0.15.7__tar.gz → 0.15.8__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 (178) hide show
  1. {python_aidot_cameras-0.15.7/python_aidot_cameras.egg-info → python_aidot_cameras-0.15.8}/PKG-INFO +1 -1
  2. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/client.py +30 -1
  3. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/pyproject.toml +1 -1
  4. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  5. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
  6. python_aidot_cameras-0.15.8/tests/test_serve_loss_tolerance.py +70 -0
  7. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/LICENSE +0 -0
  8. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/MANIFEST.in +0 -0
  9. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/README.md +0 -0
  10. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/__init__.py +0 -0
  11. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/__main__.py +0 -0
  12. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_upstream.py +0 -0
  13. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/__init__.py +0 -0
  14. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  15. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  16. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  17. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  18. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  19. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  20. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  21. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  22. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  23. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  24. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  25. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  26. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  27. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  28. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  29. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  30. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  31. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  32. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  33. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  34. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  35. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  36. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  37. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  38. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  39. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  40. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  41. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  42. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  43. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  44. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  45. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  46. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/ble_gateway.py +0 -0
  47. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/__init__.py +0 -0
  48. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/constants.py +0 -0
  49. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/controls.py +0 -0
  50. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/go2rtc.py +0 -0
  51. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/hwaccel.py +0 -0
  52. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/lan_control.py +0 -0
  53. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/models.py +0 -0
  54. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/playback.py +0 -0
  55. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/protocol.py +0 -0
  56. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/sdes.py +0 -0
  57. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/sdes_open.py +0 -0
  58. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/tutk.py +0 -0
  59. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/webrtc.py +0 -0
  60. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/camera/webrtc_open.py +0 -0
  61. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/client.py +0 -0
  62. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/const.py +0 -0
  63. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/credentials.py +0 -0
  64. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/crypto.py +0 -0
  65. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/device_client.py +0 -0
  66. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/discover.py +0 -0
  67. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/exceptions.py +0 -0
  68. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/g711.py +0 -0
  69. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/aidot_cameras/py.typed +0 -0
  70. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  71. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  72. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/python_aidot_cameras.egg-info/requires.txt +0 -0
  73. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  74. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/setup.cfg +0 -0
  75. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_aioice_compat.py +0 -0
  76. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_alarm_event.py +0 -0
  77. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_answer_sdp_video_pt.py +0 -0
  78. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_audio_mux_offline.py +0 -0
  79. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_backoff.py +0 -0
  80. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_battery_detection.py +0 -0
  81. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_battery_evidence_corroboration.py +0 -0
  82. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_battery_skip_turn.py +0 -0
  83. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_ble_gateway.py +0 -0
  84. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_bridge_state_sentinels.py +0 -0
  85. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_constants_imports_resolve.py +0 -0
  86. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_deferred_hardening.py +0 -0
  87. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_device_login_guard.py +0 -0
  88. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_device_user_info_cache.py +0 -0
  89. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dormant_camera_cleanup.py +0 -0
  90. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_mux_terminal_write.py +0 -0
  91. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_not_ready_burst.py +0 -0
  92. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_pinning.py +0 -0
  93. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_serve_decode_skip.py +0 -0
  94. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_serve_open_timeout.py +0 -0
  95. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_skip_signaling_wait.py +0 -0
  96. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_slow_probe.py +0 -0
  97. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_dtls_stdout_producer.py +0 -0
  98. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_egress_guard.py +0 -0
  99. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_env_seams.py +0 -0
  100. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_go2rtc.py +0 -0
  101. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_go2rtc_cli.py +0 -0
  102. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_h264_decode_rate_limit.py +0 -0
  103. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_highport_nomination.py +0 -0
  104. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_hwaccel_probe.py +0 -0
  105. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_ice_config_cache.py +0 -0
  106. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_ice_peer_reflexive.py +0 -0
  107. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_idle_release_both_loops.py +0 -0
  108. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_keepalive_renew.py +0 -0
  109. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_key_restart_sdp.py +0 -0
  110. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_keyframe_prompter.py +0 -0
  111. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_lan_control.py +0 -0
  112. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_light_active_color_mode.py +0 -0
  113. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_live_stream_param.py +0 -0
  114. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_live_validate_policy.py +0 -0
  115. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_logging_caps.py +0 -0
  116. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_media_socket_rcvbuf.py +0 -0
  117. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_media_wait_floor.py +0 -0
  118. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_motion_poll.py +0 -0
  119. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_mqtt_credential_refetch.py +0 -0
  120. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_mqtt_credential_self_heal.py +0 -0
  121. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_mqtt_password_not_persisted.py +0 -0
  122. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_mqtt_session_reconnect.py +0 -0
  123. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_narrow_pc_ice.py +0 -0
  124. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_no_self_referential_go2rtc.py +0 -0
  125. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_no_undefined_names.py +0 -0
  126. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_offline_keepalive_pause.py +0 -0
  127. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_open_fail_logger.py +0 -0
  128. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_open_gate_delay.py +0 -0
  129. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_peerid_reuse.py +0 -0
  130. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_persistent_mqtt.py +0 -0
  131. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_persistent_mqtt_robustness.py +0 -0
  132. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_playback_tls.py +0 -0
  133. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_post_merge_hardening.py +0 -0
  134. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_resolution_persists.py +0 -0
  135. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_restart_cycle.py +0 -0
  136. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_retry_policy.py +0 -0
  137. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_scripts_importable.py +0 -0
  138. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_adaptive.py +0 -0
  139. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_bridge_break_gate.py +0 -0
  140. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_echo_wait_timeout.py +0 -0
  141. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_fast_liveplay.py +0 -0
  142. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  143. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_idle_release.py +0 -0
  144. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_open_cleanup.py +0 -0
  145. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_serve_audio.py +0 -0
  146. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_serve_cmd.py +0 -0
  147. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_serve_stderr_drain.py +0 -0
  148. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_sprop.py +0 -0
  149. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_talk.py +0 -0
  150. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_teardown_exit_log.py +0 -0
  151. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_terminal_ack.py +0 -0
  152. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_terminal_ack_latency.py +0 -0
  153. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdes_watchdog.py +0 -0
  154. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdp_narrowing_properties.py +0 -0
  155. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sdp_payload_type_narrowing.py +0 -0
  156. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_security_hardening.py +0 -0
  157. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_serializable_login_info.py +0 -0
  158. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_serve_relay.py +0 -0
  159. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_session_stats.py +0 -0
  160. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_shared_house_seam.py +0 -0
  161. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_speak.py +0 -0
  162. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_sprop_staleness.py +0 -0
  163. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_stream_cap.py +0 -0
  164. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_stream_drain_ownership.py +0 -0
  165. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_stream_idle.py +0 -0
  166. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_stream_slot_cap.py +0 -0
  167. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_stream_teardown.py +0 -0
  168. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_talk.py +0 -0
  169. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_terminal_ack.py +0 -0
  170. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_token_file_robustness.py +0 -0
  171. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_token_refresh.py +0 -0
  172. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_turn_create_permission.py +0 -0
  173. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_upstream_compat.py +0 -0
  174. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_viewer_check_throttle.py +0 -0
  175. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_viewer_presence.py +0 -0
  176. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_viewer_present_push_mode.py +0 -0
  177. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/tests/test_wait_or_event.py +0 -0
  178. {python_aidot_cameras-0.15.7 → python_aidot_cameras-0.15.8}/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.7
3
+ Version: 0.15.8
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
@@ -365,6 +365,18 @@ def _is_self_referential_source(source: str, stream_name: str) -> bool:
365
365
  return path == stream_name
366
366
 
367
367
 
368
+ #: Packets the serve's demuxer may hold while it waits for a gap to fill. A
369
+ #: keyframe on an A001064 is 146-190 KB - roughly 130 packets - so anything
370
+ #: smaller than a few hundred cannot ride out a burst that arrives out of order.
371
+ #: Env-overridable because the right value depends on the link, not on us.
372
+ _SERVE_REORDER_QUEUE = int(os.environ.get("AIDOT_SERVE_REORDER_QUEUE", "500"))
373
+
374
+ #: How long (microseconds) the demuxer waits for a missing packet before moving
375
+ #: on. Bounded on purpose: waiting forever turns loss into a stall, which is the
376
+ #: failure this is meant to prevent.
377
+ _SERVE_MAX_DELAY_US = int(os.environ.get("AIDOT_SERVE_MAX_DELAY_US", "500000"))
378
+
379
+
368
380
  def _build_sdes_serve_cmd(
369
381
  *,
370
382
  sdp_path: str,
@@ -470,9 +482,26 @@ def _build_sdes_serve_cmd(
470
482
  # 2 s analyzeduration: the camera sends SPS+PPS+IDR in the first burst;
471
483
  # 15 s consumed nearly all packets during analysis. PLI re-arms an IDR
472
484
  # every 5 s so parameters always re-arrive.
473
- "-fflags", "+nobuffer+genpts",
485
+ #
486
+ # ``discardcorrupt`` is the loss half of this: both ends of this path are
487
+ # wireless (the camera, and typically the host), so RTP arrives with real
488
+ # gaps. Measured on the reference install: `RTP: missed N packets` with
489
+ # ZERO drops on every socket in the path, which means the packets were
490
+ # lost before they ever reached the box. Without this ffmpeg passes the
491
+ # damaged frame downstream and then backs its input queue up behind the
492
+ # reordering it thinks it needs - "max delay reached. need to consume
493
+ # packet" - and the serve eventually dies, taking every viewer with it.
494
+ # A brief artefact is a far better outcome than a dropped stream.
495
+ "-fflags", "+nobuffer+genpts+discardcorrupt",
474
496
  "-analyzeduration", "2000000",
475
497
  "-probesize", "500000",
498
+ # Reordering headroom. The default (-1) leaves the demuxer with almost
499
+ # none, so a burst that arrives out of order reads as loss. A keyframe
500
+ # here is 146-190 KB - about 130 packets - so the queue has to be able to
501
+ # hold more than one of them, and max_delay bounds how long it will wait
502
+ # before giving up on a gap rather than stalling the pipeline.
503
+ "-reorder_queue_size", str(_SERVE_REORDER_QUEUE),
504
+ "-max_delay", str(_SERVE_MAX_DELAY_US),
476
505
  "-i", sdp_path,
477
506
  *dest_args,
478
507
  ]
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "0.15.7"
7
+ version = "0.15.8"
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.7
3
+ Version: 0.15.8
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
@@ -152,6 +152,7 @@ tests/test_sdp_narrowing_properties.py
152
152
  tests/test_sdp_payload_type_narrowing.py
153
153
  tests/test_security_hardening.py
154
154
  tests/test_serializable_login_info.py
155
+ tests/test_serve_loss_tolerance.py
155
156
  tests/test_serve_relay.py
156
157
  tests/test_session_stats.py
157
158
  tests/test_shared_house_seam.py
@@ -0,0 +1,70 @@
1
+ """The serve must ride out real packet loss instead of dying on it.
2
+
3
+ Both ends of this path are usually wireless - the camera, and the Home Assistant
4
+ host - so RTP genuinely arrives with gaps. Measured on the reference install:
5
+ ffmpeg logged `RTP: missed N packets` while every socket in the path showed ZERO
6
+ kernel drops, which means those packets were lost before they ever reached the
7
+ box. Nothing in the code can recover them.
8
+
9
+ What the code controls is what happens next. Without reordering headroom the
10
+ demuxer treats a burst that arrives out of order as loss; without a bounded
11
+ max_delay it stalls waiting ("max delay reached. need to consume packet") and
12
+ backs its input queue up until the serve dies, dropping every attached viewer.
13
+ A brief artefact is a far better outcome than a dropped stream.
14
+ """
15
+ import pytest
16
+
17
+ from aidot_cameras.camera.client import (
18
+ _SERVE_MAX_DELAY_US,
19
+ _SERVE_REORDER_QUEUE,
20
+ _build_sdes_serve_cmd,
21
+ )
22
+
23
+ _DESTINATIONS = [
24
+ pytest.param({"rtsp_push_url": "rtsp://127.0.0.1:8554/cam"}, id="push"),
25
+ pytest.param({"rtsp_push_url": "http://127.0.0.1:18981/cam.ts"}, id="http-listen"),
26
+ pytest.param({"output_path": "/tmp/cam.ts"}, id="file"),
27
+ pytest.param({}, id="null-drain"),
28
+ ]
29
+
30
+
31
+ def _cmd(**kwargs):
32
+ return _build_sdes_serve_cmd(sdp_path="/tmp/x.sdp", **kwargs)
33
+
34
+
35
+ @pytest.mark.parametrize("dest", _DESTINATIONS)
36
+ def test_every_destination_gets_the_loss_tolerance_args(dest):
37
+ """The input side is shared, so loss tolerance must not depend on the sink."""
38
+ cmd = _cmd(**dest)
39
+ assert "-reorder_queue_size" in cmd
40
+ assert cmd[cmd.index("-reorder_queue_size") + 1] == str(_SERVE_REORDER_QUEUE)
41
+ assert "-max_delay" in cmd
42
+ assert cmd[cmd.index("-max_delay") + 1] == str(_SERVE_MAX_DELAY_US)
43
+ fflags = cmd[cmd.index("-fflags") + 1]
44
+ assert "discardcorrupt" in fflags, fflags
45
+
46
+
47
+ def test_the_reorder_queue_clears_a_keyframe():
48
+ # 146-190 KB of keyframe is ~130 packets at MTU. A queue that cannot hold
49
+ # more than one keyframe's worth reads an out-of-order burst as loss.
50
+ assert _SERVE_REORDER_QUEUE >= 260
51
+
52
+
53
+ def test_max_delay_is_bounded():
54
+ """Unbounded waiting turns loss into the stall this is meant to prevent."""
55
+ assert 0 < _SERVE_MAX_DELAY_US <= 2_000_000
56
+
57
+
58
+ @pytest.mark.parametrize("dest", _DESTINATIONS)
59
+ def test_the_low_latency_flags_survive(dest):
60
+ """Tolerance must not be bought by silently reintroducing buffering."""
61
+ fflags = _cmd(**dest)[_cmd(**dest).index("-fflags") + 1]
62
+ assert "nobuffer" in fflags and "genpts" in fflags, fflags
63
+
64
+
65
+ def test_the_args_sit_before_the_input():
66
+ """Demuxer options after -i apply to the output and are silently ignored."""
67
+ cmd = _cmd(rtsp_push_url="rtsp://127.0.0.1:8554/cam")
68
+ i = cmd.index("-i")
69
+ for opt in ("-reorder_queue_size", "-max_delay", "-fflags"):
70
+ assert cmd.index(opt) < i, f"{opt} must precede -i to affect the input"