python-aidot-cameras 1.0.0rc66__tar.gz → 1.0.0rc67__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 (365) hide show
  1. {python_aidot_cameras-1.0.0rc66/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0rc67}/PKG-INFO +1 -1
  2. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/recording.py +8 -0
  3. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/sd_playback.py +67 -3
  4. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/webrtc_open.py +54 -12
  5. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/pyproject.toml +1 -1
  6. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
  7. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_dtls.py +101 -0
  8. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/LICENSE +0 -0
  9. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/MANIFEST.in +0 -0
  10. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/README.md +0 -0
  11. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/__init__.py +0 -0
  12. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/__main__.py +0 -0
  13. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_log_redaction.py +0 -0
  14. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_upstream.py +0 -0
  15. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/__init__.py +0 -0
  16. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/LICENSE +0 -0
  17. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
  18. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
  19. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
  20. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
  21. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
  22. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
  23. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
  24. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
  25. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
  26. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
  27. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
  28. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
  29. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/events.py +0 -0
  30. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
  31. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
  32. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
  33. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
  34. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
  35. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
  36. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
  37. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
  38. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
  39. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
  40. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
  41. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
  42. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
  43. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
  44. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
  45. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
  46. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
  47. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
  48. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
  49. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/__init__.py +0 -0
  50. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/aac_track.py +0 -0
  51. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/agc.py +0 -0
  52. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/client.py +0 -0
  53. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/constants.py +0 -0
  54. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/controls.py +0 -0
  55. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/go2rtc.py +0 -0
  56. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/h264_sps.py +0 -0
  57. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/health.py +0 -0
  58. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/hls_ts.py +0 -0
  59. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/hwaccel.py +0 -0
  60. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/lan_control.py +0 -0
  61. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/models.py +0 -0
  62. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/protocol.py +0 -0
  63. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/rtp_h264.py +0 -0
  64. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/rtsp_publish.py +0 -0
  65. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/sd_events.py +0 -0
  66. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/sd_listing.py +0 -0
  67. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/sdes.py +0 -0
  68. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/sdes_open.py +0 -0
  69. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/ts_fanout.py +0 -0
  70. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/ts_tee.py +0 -0
  71. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/camera/webrtc.py +0 -0
  72. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/client.py +0 -0
  73. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/cloud_auth.py +0 -0
  74. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/const.py +0 -0
  75. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/credentials.py +0 -0
  76. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/crypto.py +0 -0
  77. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/device_client.py +0 -0
  78. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/discover.py +0 -0
  79. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/exceptions.py +0 -0
  80. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/g711.py +0 -0
  81. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/lan_retry.py +0 -0
  82. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/py.typed +0 -0
  83. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/aidot_cameras/settings.py +0 -0
  84. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/python_aidot_cameras.egg-info/SOURCES.txt +0 -0
  85. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
  86. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
  87. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/python_aidot_cameras.egg-info/requires.txt +0 -0
  88. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/python_aidot_cameras.egg-info/top_level.txt +0 -0
  89. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/setup.cfg +0 -0
  90. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_aac_track.py +0 -0
  91. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_abandoned_attempt_skips_the_doomed_serve.py +0 -0
  92. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_abandoned_wait_still_maps_audio.py +0 -0
  93. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_aioice_compat.py +0 -0
  94. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_alarm_event.py +0 -0
  95. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_answer_pt_kinds.py +0 -0
  96. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_answer_sdp_video_pt.py +0 -0
  97. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_answer_section_selection.py +0 -0
  98. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_arrival_timestamps.py +0 -0
  99. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_audio_mux_offline.py +0 -0
  100. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_auth_error_routing.py +0 -0
  101. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_auto_alarm.py +0 -0
  102. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_auto_light_settings.py +0 -0
  103. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_avio_ctrl_uses_the_session_dseq.py +0 -0
  104. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_avio_header_single_layout.py +0 -0
  105. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_avio_request.py +0 -0
  106. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_avio_response_parse.py +0 -0
  107. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_avio_response_router.py +0 -0
  108. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_backoff.py +0 -0
  109. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_battery_cap_spans_sessions.py +0 -0
  110. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_battery_detection.py +0 -0
  111. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_battery_evidence_corroboration.py +0 -0
  112. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_battery_stale_offer_is_abandoned_early.py +0 -0
  113. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_battery_wake_settles_before_the_command.py +0 -0
  114. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ble_gateway_is_gone.py +0 -0
  115. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_bridge_state_sentinels.py +0 -0
  116. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_busy_backoff.py +0 -0
  117. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_camera_properties.py +0 -0
  118. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_canary_log_names_the_camera.py +0 -0
  119. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_close_mid_ice_leaves_nothing_pending.py +0 -0
  120. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_cloud_count.py +0 -0
  121. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_cloud_error_log_names_the_exception.py +0 -0
  122. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_cloud_error_severity.py +0 -0
  123. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_cloud_plan.py +0 -0
  124. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_constants_imports_resolve.py +0 -0
  125. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_control_verdicts.py +0 -0
  126. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_declared_properties.py +0 -0
  127. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_deferred_hardening.py +0 -0
  128. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_detection_types.py +0 -0
  129. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_device_cmd_seq_match.py +0 -0
  130. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_device_login_guard.py +0 -0
  131. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_device_user_info_cache.py +0 -0
  132. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_direct_publish_attaches_negotiated_audio.py +0 -0
  133. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_direct_ts_keyframe_signal.py +0 -0
  134. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_direct_ts_serve.py +0 -0
  135. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_direct_ts_serve_only_for_http.py +0 -0
  136. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dormant_camera_cleanup.py +0 -0
  137. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_drain_is_cancellable.py +0 -0
  138. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_mux_terminal_write.py +0 -0
  139. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_not_ready_burst.py +0 -0
  140. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_open_abort_covers_every_exit.py +0 -0
  141. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_pinning.py +0 -0
  142. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_recorder_wiring.py +0 -0
  143. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_refusal_after_the_echo.py +0 -0
  144. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_resolution_ack.py +0 -0
  145. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_serve_decode_skip.py +0 -0
  146. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_serve_ice_wait.py +0 -0
  147. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_serve_open_timeout.py +0 -0
  148. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_skip_signaling_wait.py +0 -0
  149. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_slow_probe.py +0 -0
  150. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_stdout_producer.py +0 -0
  151. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_tap_states_no_reorder.py +0 -0
  152. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_ts_copy_recording.py +0 -0
  153. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_dtls_video_presence.py +0 -0
  154. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_egress_guard.py +0 -0
  155. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_env_seams.py +0 -0
  156. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_every_exception_is_exported.py +0 -0
  157. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_feature_probe_needs_a_live_session.py +0 -0
  158. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ffmpeg_rx_sdp_single_template.py +0 -0
  159. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ffmpeg_signal_exit_is_not_a_crash.py +0 -0
  160. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_first_media_abandon_rules.py +0 -0
  161. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_futile_keepalive.py +0 -0
  162. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_gate_requires_a_decoded_frame.py +0 -0
  163. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_go2rtc.py +0 -0
  164. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_go2rtc_cli.py +0 -0
  165. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_h264_decode_rate_limit.py +0 -0
  166. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_h264_sps.py +0 -0
  167. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_health_line.py +0 -0
  168. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_health_line_counts_pictures.py +0 -0
  169. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_highport_nomination.py +0 -0
  170. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_hls_ts_end_to_end.py +0 -0
  171. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_hls_ts_wiring.py +0 -0
  172. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_hwaccel_probe.py +0 -0
  173. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ice_config_cache.py +0 -0
  174. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ice_credentials_match_the_app.py +0 -0
  175. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ice_peer_reflexive.py +0 -0
  176. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_idle_release_both_loops.py +0 -0
  177. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ipc_envelope_single_builder.py +0 -0
  178. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_keepalive_renew.py +0 -0
  179. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_key_restart_sdp.py +0 -0
  180. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_keyframe_prompter.py +0 -0
  181. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_control.py +0 -0
  182. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_login_accessors.py +0 -0
  183. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
  184. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_retry_short_sessions.py +0 -0
  185. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_retry_silences_upstream_over_ceiling.py +0 -0
  186. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_retry_stops_on_close.py +0 -0
  187. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_lan_sweep_only_with_lights.py +0 -0
  188. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_late_repeats_are_dropped.py +0 -0
  189. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_light_active_color_mode.py +0 -0
  190. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_live_session_carries_talk.py +0 -0
  191. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_live_stream_param.py +0 -0
  192. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_live_validate_policy.py +0 -0
  193. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_live_validate_token_refresh.py +0 -0
  194. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_live_video_canary.py +0 -0
  195. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_liveplay_echo_timeout.py +0 -0
  196. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_local_only_controls.py +0 -0
  197. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_log_hygiene.py +0 -0
  198. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_logging_caps.py +0 -0
  199. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_login_connect_deadline.py +0 -0
  200. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_login_reconnect_backoff.py +0 -0
  201. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_loop_break_ends_the_loop.py +0 -0
  202. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_media_counted_only_when_plaintext.py +0 -0
  203. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_media_path_is_reported.py +0 -0
  204. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_media_socket_rcvbuf.py +0 -0
  205. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_media_wait_floor.py +0 -0
  206. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_motion_poll.py +0 -0
  207. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_client_id_is_exact.py +0 -0
  208. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_credential_refetch.py +0 -0
  209. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_credential_self_heal.py +0 -0
  210. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_password_not_persisted.py +0 -0
  211. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_session_reconnect.py +0 -0
  212. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mqtt_thread_not_orphaned.py +0 -0
  213. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_mypy_new_findings.py +0 -0
  214. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_nack.py +0 -0
  215. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_nack_cannot_add_load_in_a_burst.py +0 -0
  216. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_narrow_pc_ice.py +0 -0
  217. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_no_key_material_in_logs.py +0 -0
  218. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_no_private_notes_on_the_public_surface.py +0 -0
  219. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_no_self_referential_go2rtc.py +0 -0
  220. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_no_unbounded_hex_dumps.py +0 -0
  221. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_no_undefined_names.py +0 -0
  222. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_nominates_as_soon_as_the_answer_lands.py +0 -0
  223. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_off_subnet_candidate.py +0 -0
  224. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
  225. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_offline_keepalive_pause.py +0 -0
  226. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_on_frame_is_an_av_frame.py +0 -0
  227. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_open_errors_are_aidot_errors.py +0 -0
  228. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_open_fail_logger.py +0 -0
  229. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_open_gate_delay.py +0 -0
  230. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_peer_id_client_type.py +0 -0
  231. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_peerid_reuse.py +0 -0
  232. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_persistent_mqtt.py +0 -0
  233. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_persistent_mqtt_robustness.py +0 -0
  234. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_phase_returns_reach_the_caller.py +0 -0
  235. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_phase_state_is_complete.py +0 -0
  236. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_plaintext_mqtt_refused.py +0 -0
  237. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_post_merge_hardening.py +0 -0
  238. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ptz_probe_reads_what_it_is_told.py +0 -0
  239. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_public_signature_matches_impl.py +0 -0
  240. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_publish_target_readiness.py +0 -0
  241. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_quality_campaign_in_session.py +0 -0
  242. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_reboot_action.py +0 -0
  243. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_recent_recordings.py +0 -0
  244. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_region_allowlist.py +0 -0
  245. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_remb.py +0 -0
  246. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_remb_goes_out_the_relay_aware_sender.py +0 -0
  247. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_removed_keepalive_arguments_are_tolerated.py +0 -0
  248. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_resent_frames_are_dropped.py +0 -0
  249. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_resolution_persists.py +0 -0
  250. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_restart_cycle.py +0 -0
  251. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_retry_policy.py +0 -0
  252. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_review_lan_retry_and_graces.py +0 -0
  253. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_rtp_h264.py +0 -0
  254. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_rtsp_publish.py +0 -0
  255. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_rtsp_publish_go2rtc.py +0 -0
  256. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_scripts_importable.py +0 -0
  257. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sctp_init_ack_tsn.py +0 -0
  258. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sctp_reassembly.py +0 -0
  259. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sctp_sack.py +0 -0
  260. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_card_presence.py +0 -0
  261. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_event_decode.py +0 -0
  262. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_event_probe_payloads.py +0 -0
  263. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_listing.py +0 -0
  264. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback.py +0 -0
  265. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_clock.py +0 -0
  266. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_controls.py +0 -0
  267. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_entry.py +0 -0
  268. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_media_hold.py +0 -0
  269. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sd_playback_open.py +0 -0
  270. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_avio_dispatch.py +0 -0
  271. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_bridge_break_gate.py +0 -0
  272. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_connection_mode.py +0 -0
  273. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_echo_wait_is_not_dead_time.py +0 -0
  274. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_fast_liveplay.py +0 -0
  275. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
  276. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_first_media_stall_report.py +0 -0
  277. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_ice_teardown.py +0 -0
  278. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_idle_release.py +0 -0
  279. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_nack_bridge.py +0 -0
  280. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_offer_video_pin.py +0 -0
  281. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_open_cleanup.py +0 -0
  282. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_open_yields_the_loop.py +0 -0
  283. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_pinned_codec_retry.py +0 -0
  284. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_sctp_abort.py +0 -0
  285. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_serve_audio.py +0 -0
  286. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_serve_cmd.py +0 -0
  287. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_serve_stderr_drain.py +0 -0
  288. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_sprop.py +0 -0
  289. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_talk.py +0 -0
  290. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_teardown_exit_log.py +0 -0
  291. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_terminal_ack.py +0 -0
  292. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_terminal_ack_latency.py +0 -0
  293. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdes_watchdog.py +0 -0
  294. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdp_narrowing_properties.py +0 -0
  295. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sdp_payload_type_narrowing.py +0 -0
  296. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_security_hardening.py +0 -0
  297. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_self_peer_is_an_address_not_an_ip.py +0 -0
  298. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serializable_login_info.py +0 -0
  299. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_bound_survives_arrival_stamping.py +0 -0
  300. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_input_timeout.py +0 -0
  301. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_loop_releases_on_error.py +0 -0
  302. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_loss_tolerance.py +0 -0
  303. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_pt_respects_the_pin.py +0 -0
  304. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_relay.py +0 -0
  305. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_serve_stderr_keeps_the_reason.py +0 -0
  306. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_session_mode_seq.py +0 -0
  307. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_session_stats.py +0 -0
  308. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_session_stop_twice.py +0 -0
  309. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_settings_registry.py +0 -0
  310. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_shared_house_seam.py +0 -0
  311. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_signalling_is_attributable_to_a_camera.py +0 -0
  312. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_skip_guard.py +0 -0
  313. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_snapshot_cancellation.py +0 -0
  314. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_speak.py +0 -0
  315. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sprop_capture_gate.py +0 -0
  316. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sprop_staleness.py +0 -0
  317. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sprop_unstable_public.py +0 -0
  318. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_sprop_write_is_reported_truthfully.py +0 -0
  319. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_srtp_rx_rekey.py +0 -0
  320. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_srtp_tx_key_note.py +0 -0
  321. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stable_terminal_id.py +0 -0
  322. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_nudge_policy.py +0 -0
  323. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_report_says_what_the_answer_carried.py +0 -0
  324. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_report_says_whether_the_trigger_was_answered.py +0 -0
  325. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_report_separates_silence_from_undecryptable.py +0 -0
  326. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_report_survives_a_caller_giving_up.py +0 -0
  327. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stall_reports_reach_the_artifact.py +0 -0
  328. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_status_and_trace_channels.py +0 -0
  329. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stream_cap.py +0 -0
  330. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stream_drain_ownership.py +0 -0
  331. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stream_idle.py +0 -0
  332. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stream_slot_cap.py +0 -0
  333. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stream_teardown.py +0 -0
  334. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_stun_attr_single_builder.py +0 -0
  335. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_swallowed_exception_names.py +0 -0
  336. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_talk.py +0 -0
  337. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_talk_reports_a_speaker_that_never_opened.py +0 -0
  338. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_tap_to_mux_end_to_end.py +0 -0
  339. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_terminal_ack.py +0 -0
  340. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_tmmbr.py +0 -0
  341. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_token_file_robustness.py +0 -0
  342. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_token_refresh.py +0 -0
  343. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_transport_failure_reporting.py +0 -0
  344. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ts_fanout.py +0 -0
  345. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_ts_tee.py +0 -0
  346. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_turn_create_permission.py +0 -0
  347. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_turn_target_shared.py +0 -0
  348. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_unbuildable_summary_repeats_at_debug.py +0 -0
  349. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_unreachable_nominee_is_abandoned_early.py +0 -0
  350. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_upstream_compat.py +0 -0
  351. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_upstream_seam_adapter.py +0 -0
  352. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_upstream_secrets_redacted.py +0 -0
  353. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_validator_shares_one_token.py +0 -0
  354. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_vendored_aiortc_integrity.py +0 -0
  355. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_video_presence_needs_a_keyframe.py +0 -0
  356. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_video_profile_log.py +0 -0
  357. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_video_pts_dts.py +0 -0
  358. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_video_ts_unwrap.py +0 -0
  359. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_viewer_check_throttle.py +0 -0
  360. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_viewer_presence.py +0 -0
  361. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_viewer_present_push_mode.py +0 -0
  362. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_wait_or_event.py +0 -0
  363. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_wake_gate_camera_evidence.py +0 -0
  364. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_wake_readiness_retry.py +0 -0
  365. {python_aidot_cameras-1.0.0rc66 → python_aidot_cameras-1.0.0rc67}/tests/test_webrtc_answer_race.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-aidot-cameras
3
- Version: 1.0.0rc66
3
+ Version: 1.0.0rc67
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
@@ -68,6 +68,9 @@ class TsCopyRecorder:
68
68
  self._thread: Optional[threading.Thread] = None
69
69
  self._state: dict = {}
70
70
  self._stopped = False
71
+ # Called first thing in stop(), for whoever still holds packets for
72
+ # this recording (the SD playback tap's reorder buffer).
73
+ self.on_stop: "list" = []
71
74
  # Opened before the session connects, so an unwritable path fails
72
75
  # while there is nothing to clean up: by the caller in an executor
73
76
  # (``file``; an open() on an event loop blocks it), or here. Held open
@@ -96,6 +99,11 @@ class TsCopyRecorder:
96
99
  if self._stopped:
97
100
  return
98
101
  self._stopped = True
102
+ for _cb in self.on_stop:
103
+ try:
104
+ _cb()
105
+ except Exception:
106
+ _LOGGER.debug("recording stop callback failed", exc_info=True)
99
107
  if self._thread is None:
100
108
  self._close()
101
109
  return
@@ -597,6 +597,59 @@ def answer_h265_parameter_sets(sdp: str, pt: Optional[int]) -> "list[bytes]":
597
597
  return out
598
598
 
599
599
 
600
+ class SeqReorder:
601
+ """Puts one RTP stream back in sequence order, waiting a little for gaps.
602
+
603
+ A packet the camera resends after a NACK arrives after the ones that
604
+ followed it; the access-unit assembler takes packets in order, so they
605
+ are held here until the gap fills or ``max_wait_s`` passes, then handed
606
+ on in order (a gap given up on reaches the assembler as a gap, and its
607
+ unit is handed on damaged). ``push`` returns what is ready; ``flush``
608
+ returns the rest at the end.
609
+ """
610
+
611
+ #: Most packets held; past this the oldest gap is given up at once.
612
+ MAX_HELD = 1500
613
+
614
+ def __init__(self, max_wait_s: float = 0.3) -> None:
615
+ self.max_wait_s = max_wait_s
616
+ self._next: "Optional[int]" = None
617
+ self._held: "dict[int, tuple[float, Any]]" = {}
618
+ self.late = 0 # arrived after its gap was given up
619
+
620
+ def push(self, seq: int, item: Any, now: float) -> "list":
621
+ if self._next is None:
622
+ self._next = seq
623
+ if (seq - self._next) & 0x8000: # before the next expected one
624
+ self.late += 1
625
+ return []
626
+ self._held.setdefault(seq, (now, item))
627
+ return self._release(now)
628
+
629
+ def _release(self, now: float) -> "list":
630
+ out = []
631
+ while True:
632
+ while self._next in self._held:
633
+ out.append(self._held.pop(self._next)[1])
634
+ self._next = (self._next + 1) & 0xFFFF
635
+ if not self._held:
636
+ return out
637
+ oldest = min(t for t, _ in self._held.values())
638
+ if now - oldest < self.max_wait_s and len(self._held) < self.MAX_HELD:
639
+ return out
640
+ # Give up on the gap: carry on from the earliest packet held.
641
+ self._next = min(self._held, key=lambda s: (s - self._next) & 0xFFFF)
642
+
643
+ def flush(self) -> "list":
644
+ out = []
645
+ while self._held:
646
+ if self._next not in self._held:
647
+ self._next = min(self._held, key=lambda s: (s - self._next) & 0xFFFF)
648
+ out.append(self._held.pop(self._next)[1])
649
+ self._next = (self._next + 1) & 0xFFFF
650
+ return out
651
+
652
+
600
653
  class H265AccessUnits:
601
654
  """RTP payloads (RFC 7798) in, Annex B access units out.
602
655
 
@@ -627,6 +680,12 @@ class H265AccessUnits:
627
680
  self._broken = False
628
681
  self.dropped = 0
629
682
  self.damaged = 0
683
+ # The camera sends a recording's last frame twice, under a new
684
+ # timestamp (A000088, 2026-10-30): a decoder then sees the same picture
685
+ # order count twice. A unit identical to the one before is not handed
686
+ # on (``repeats`` counts them).
687
+ self.repeats = 0
688
+ self._last_unit: "Optional[bytes]" = None
630
689
 
631
690
  def _emit(self) -> "list[tuple[bytes, int, bool]]":
632
691
  out = []
@@ -641,9 +700,14 @@ class H265AccessUnits:
641
700
  nals = self._nals
642
701
  if kf and 32 not in types and self.parameter_sets:
643
702
  nals = self.parameter_sets + nals
644
- out.append((b"".join(_NAL_SC + n for n in nals), self._ts, kf))
645
- if self._broken:
646
- self.damaged += 1
703
+ unit = b"".join(_NAL_SC + n for n in nals)
704
+ if unit == self._last_unit:
705
+ self.repeats += 1
706
+ else:
707
+ out.append((unit, self._ts, kf))
708
+ self._last_unit = unit
709
+ if self._broken:
710
+ self.damaged += 1
647
711
  elif self._nals or self._broken:
648
712
  self.dropped += 1
649
713
  self._nals, self._fu, self._broken = [], None, False
@@ -295,7 +295,8 @@ def _install_sd_h265_tap(pc, h265, status) -> int:
295
295
  PCMU by number, but its answer's audio was PCMA alone, so nothing else
296
296
  claims it. Returns the routers tapped.
297
297
  """
298
- from .sd_playback import starts_h265_stream
298
+ from .._vendor.aiortc.rtcrtpreceiver import NackGenerator
299
+ from .sd_playback import SeqReorder, starts_h265_stream
299
300
 
300
301
  if h265 is None:
301
302
  return 0
@@ -308,17 +309,21 @@ def _install_sd_h265_tap(pc, h265, status) -> int:
308
309
  routers.append(router)
309
310
 
310
311
  def _make(router):
311
- def _tap(packet) -> None:
312
- pt = h265["pt"]
313
- if pt is None:
314
- if packet.payload_type in router.payload_type_table or not (
315
- starts_h265_stream(packet.payload)
316
- ):
317
- return
318
- h265["pt"] = pt = packet.payload_type
319
- status(f"SD playback: taking pt={pt} as the recording's H265")
320
- if packet.payload_type != pt:
321
- return
312
+ # A video receiver on this transport sends the NACKs: aiortc only
313
+ # asks for resends on streams it routes, and this one it does not.
314
+ rx = None
315
+ for t in pc.getTransceivers():
316
+ r = t.receiver
317
+ if getattr(getattr(r, "transport", None), "_rtp_router", None) is router:
318
+ if rx is None or getattr(t, "kind", None) == "video":
319
+ rx = r
320
+ reorder = SeqReorder()
321
+ nack_gen = NackGenerator()
322
+ # The loop holds tasks weakly: a NACK not referenced here can be
323
+ # collected before it is sent.
324
+ nack_tasks: "set" = set()
325
+
326
+ def _feed(packet) -> None:
322
327
  units = h265["units"]
323
328
  lost_before = units.dropped
324
329
  damaged_before = units.damaged
@@ -342,6 +347,43 @@ def _install_sd_h265_tap(pc, h265, status) -> int:
342
347
  " damaged (the decoder conceals it)"
343
348
  )
344
349
 
350
+ def _flush() -> None:
351
+ for held in reorder.flush():
352
+ _feed(held)
353
+
354
+ on_stop = getattr(h265["recorder"], "on_stop", None)
355
+ if on_stop is not None:
356
+ on_stop.append(_flush)
357
+
358
+ def _tap(packet) -> None:
359
+ pt = h265["pt"]
360
+ if pt is None:
361
+ if packet.payload_type in router.payload_type_table or not (
362
+ starts_h265_stream(packet.payload)
363
+ ):
364
+ return
365
+ h265["pt"] = pt = packet.payload_type
366
+ status(f"SD playback: taking pt={pt} as the recording's H265")
367
+ if packet.payload_type != pt:
368
+ return
369
+ # Ask for what never arrived; the resend is put back in place
370
+ # by the reorder buffer before the assembler sees it.
371
+ if nack_gen.add(packet) and rx is not None and nack_gen.missing:
372
+ h265["nacks"] = h265.get("nacks", 0) + 1
373
+ if h265["nacks"] == 1:
374
+ status("SD playback: asking the camera to resend lost H265 (NACK)")
375
+ try:
376
+ task = asyncio.get_running_loop().create_task(
377
+ rx._send_rtcp_nack(packet.ssrc, sorted(nack_gen.missing))
378
+ )
379
+ except RuntimeError:
380
+ pass # no loop: nothing to send on
381
+ else:
382
+ nack_tasks.add(task)
383
+ task.add_done_callback(nack_tasks.discard)
384
+ for ready in reorder.push(packet.sequence_number, packet, time.monotonic()):
385
+ _feed(ready)
386
+
345
387
  return _tap
346
388
 
347
389
  for router in routers:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-aidot-cameras"
7
- version = "1.0.0rc66"
7
+ version = "1.0.0rc67"
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: 1.0.0rc66
3
+ Version: 1.0.0rc67
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
@@ -474,3 +474,104 @@ def test_by_default_a_damaged_unit_is_handed_on_and_counted():
474
474
  assert units.damaged == 1 and units.dropped == 0
475
475
  assert got[0][2] is True # still marked a keyframe
476
476
  assert got[1:] and [_nals(d) for d, _, _ in got[1:]] == [_nals(a) for a in aus[1:]]
477
+
478
+
479
+ def test_a_unit_sent_twice_is_handed_on_once():
480
+ # The A000088 sent a recording's last frame twice under a new timestamp
481
+ # (2026-10-30); the decoder then reported a duplicate picture order count.
482
+ aus = _hevc_access_units(3)
483
+ units = H265AccessUnits()
484
+ got, seq = [], 10
485
+ for i, au in enumerate([*aus, aus[-1]]):
486
+ pkts = _packetize(au, i * 6000, seq)
487
+ seq += len(pkts)
488
+ for p in pkts:
489
+ got += units.feed(*p)
490
+ assert [ts for _, ts, _ in got] == [0, 6000, 12000]
491
+ assert units.repeats == 1
492
+
493
+
494
+ def test_a_resent_packet_is_put_back_in_place():
495
+ from aidot_cameras.camera.sd_playback import SeqReorder
496
+
497
+ r = SeqReorder(max_wait_s=0.3)
498
+ assert r.push(10, "a", 0.0) == ["a"]
499
+ assert r.push(12, "c", 0.01) == [] # 11 missing: hold 12
500
+ assert r.push(13, "d", 0.02) == []
501
+ assert r.push(11, "b", 0.1) == ["b", "c", "d"] # the resend fills it
502
+
503
+
504
+ def test_a_gap_that_never_fills_is_given_up_in_time():
505
+ from aidot_cameras.camera.sd_playback import SeqReorder
506
+
507
+ r = SeqReorder(max_wait_s=0.3)
508
+ r.push(65534, "a", 0.0)
509
+ assert r.push(1, "d", 0.01) == [] # 65535 and 0 missing, across the wrap
510
+ assert r.push(2, "e", 0.35) == ["d", "e"] # past the wait: carry on
511
+ assert r.push(0, "late", 0.4) == [] and r.late == 1
512
+ r.push(5, "h", 0.5)
513
+ assert r.flush() == ["h"]
514
+
515
+
516
+ def test_the_tap_asks_for_a_lost_packet_and_puts_the_resend_in_place():
517
+ # The A000088 resends what a NACK names (2026-10-30: three plays, no
518
+ # frame damaged or dropped, 0 decode errors, against 147 error lines
519
+ # from one dropped frame before). aiortc does not NACK this stream, as it
520
+ # does not route it; the tap asks, and holds packets until the resend.
521
+ import asyncio
522
+
523
+ from aidot_cameras.camera.webrtc_open import _install_sd_h265_tap
524
+
525
+ sent = []
526
+
527
+ class _Rx:
528
+ def __init__(self, router):
529
+ self.transport = type("T", (), {"_rtp_router": router})()
530
+
531
+ async def _send_rtcp_nack(self, ssrc, lost):
532
+ sent.append((ssrc, list(lost)))
533
+
534
+ class _PcNack:
535
+ def __init__(self, router):
536
+ self._t = [type("Tr", (), {"receiver": _Rx(router), "kind": "video"})()]
537
+
538
+ def getTransceivers(self):
539
+ return self._t
540
+
541
+ def _p(seq, ts, marker, payload):
542
+ p = _pkt(98, seq, ts, marker, payload)
543
+ p.ssrc = 0x1234
544
+ return p
545
+
546
+ rec = type("Rec", (), {"vq": queue.Queue(), "on_stop": []})()
547
+ state = {"pt": 98, "units": H265AccessUnits(), "recorder": rec, "dropped": 0}
548
+ router = _Router()
549
+
550
+ # One IDR in three fragments, and the next unit.
551
+ idr = bytes([19 << 1, 1]) + bytes(range(60))
552
+ fu_ind = bytes([(idr[0] & 0x81) | (49 << 1), idr[1]])
553
+ body = idr[2:]
554
+ frags = [
555
+ fu_ind + bytes([19 | 0x80]) + body[:20],
556
+ fu_ind + bytes([19]) + body[20:40],
557
+ fu_ind + bytes([19 | 0x40]) + body[40:],
558
+ ]
559
+ trail = bytes([1 << 1, 1, 0xBB])
560
+
561
+ async def main():
562
+ assert _install_sd_h265_tap(_PcNack(router), state, lambda m: None) == 1
563
+ router.raw_tap(_p(100, 3000, False, frags[0]))
564
+ router.raw_tap(_p(102, 3000, True, frags[2])) # 101 lost
565
+ router.raw_tap(_p(103, 9000, True, trail))
566
+ await asyncio.sleep(0)
567
+ assert sent == [(0x1234, [101])]
568
+ assert rec.vq.empty() # held for the resend
569
+ router.raw_tap(_p(101, 3000, False, frags[1])) # the resend
570
+ await asyncio.sleep(0)
571
+
572
+ asyncio.run(main())
573
+ first = rec.vq.get_nowait()
574
+ assert first == (b"\x00\x00\x00\x01" + idr, 3000, True, "hevc")
575
+ assert state["units"].damaged == 0 and state["units"].dropped == 0
576
+ assert rec.vq.get_nowait()[1] == 9000
577
+ assert rec.on_stop # the tap flushes what it holds when the recording stops