python-aidot-cameras 0.17.3b2__tar.gz → 1.0.0b3__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.
- {python_aidot_cameras-0.17.3b2/python_aidot_cameras.egg-info → python_aidot_cameras-1.0.0b3}/PKG-INFO +3 -1
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/README.md +2 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/client.py +29 -4
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/protocol.py +12 -4
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/sdes.py +28 -8
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/sdes_open.py +432 -25
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/pyproject.toml +1 -1
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3/python_aidot_cameras.egg-info}/PKG-INFO +3 -1
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/python_aidot_cameras.egg-info/SOURCES.txt +11 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_control_verdicts.py +12 -2
- python_aidot_cameras-1.0.0b3/tests/test_dtls_resolution_ack.py +238 -0
- python_aidot_cameras-1.0.0b3/tests/test_feature_probe_needs_a_live_session.py +132 -0
- python_aidot_cameras-1.0.0b3/tests/test_gate_requires_a_decoded_frame.py +101 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_ice_peer_reflexive.py +7 -3
- python_aidot_cameras-1.0.0b3/tests/test_live_session_carries_talk.py +113 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_live_validate_policy.py +182 -0
- python_aidot_cameras-1.0.0b3/tests/test_no_key_material_in_logs.py +269 -0
- python_aidot_cameras-1.0.0b3/tests/test_ptz_probe_reads_what_it_is_told.py +88 -0
- python_aidot_cameras-1.0.0b3/tests/test_sdes_first_media_stall_report.py +344 -0
- python_aidot_cameras-1.0.0b3/tests/test_sdes_offer_video_order.py +263 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_talk.py +16 -0
- python_aidot_cameras-1.0.0b3/tests/test_self_peer_is_an_address_not_an_ip.py +101 -0
- python_aidot_cameras-1.0.0b3/tests/test_sprop_write_is_reported_truthfully.py +102 -0
- python_aidot_cameras-1.0.0b3/tests/test_talk_reports_a_speaker_that_never_opened.py +170 -0
- python_aidot_cameras-1.0.0b3/tests/test_tutk_path_is_out_of_reach.py +78 -0
- python_aidot_cameras-0.17.3b2/tests/test_no_key_material_in_logs.py +0 -140
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/LICENSE +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_upstream.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/ble_gateway.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/hwaccel.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/lan_retry.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/setup.cfg +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_answer_sdp_video_pt.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_audio_mux_offline.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_avio_request.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_avio_response_parse.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_avio_response_router.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_battery_detection.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_battery_evidence_corroboration.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_battery_skip_turn.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_ble_gateway.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_bridge_state_sentinels.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_busy_backoff.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_constants_imports_resolve.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_device_cmd_seq_match.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dormant_camera_cleanup.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_mux_terminal_write.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_dtls_stdout_producer.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_env_seams.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_futile_keepalive.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_hwaccel_probe.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_idle_release_both_loops.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_key_restart_sdp.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_lan_retry_reaches_the_storming_devices.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_login_connect_deadline.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_login_reconnect_backoff.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_media_counted_only_when_plaintext.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_media_socket_rcvbuf.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_media_wait_floor.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_no_self_referential_go2rtc.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_offer_keeps_rtcp_feedback.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_peerid_reuse.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_remb.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_resolution_persists.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_restart_cycle.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_scripts_importable.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sctp_init_ack_tsn.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_avio_dispatch.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_offer_video_pin.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_sctp_abort.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_serve_audio.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_serve_stderr_drain.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_terminal_ack.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_terminal_ack_latency.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdp_narrowing_properties.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sdp_payload_type_narrowing.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_serve_loss_tolerance.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_shared_house_seam.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_snapshot_cancellation.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sprop_capture_gate.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_sprop_staleness.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_srtp_rx_rekey.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_srtp_tx_key_note.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_stream_slot_cap.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_turn_create_permission.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_video_profile_log.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_viewer_check_throttle.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_viewer_presence.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_viewer_present_push_mode.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_wait_or_event.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_wake_gate_camera_evidence.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/tests/test_wake_readiness_retry.py +0 -0
- {python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/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: 0.
|
|
3
|
+
Version: 1.0.0b3
|
|
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
|
|
@@ -209,6 +209,8 @@ audio, idle release, the sprop cache path) are documented in
|
|
|
209
209
|
| `AIDOT_SDES_SKIP_TURN_PREALLOC` | Skip the SDES TURN relay pre-allocation (~2-3 s of cold-start latency) so signaling goes straight out with the host candidate. Faster on a LAN, at the cost of no relay fallback for a camera on a different segment / behind strict NAT. Experimental, opt-in (truthy = `1`/`true`/`yes`/`on`). | unset (off) |
|
|
210
210
|
| `AIDOT_SDES_ADAPTIVE` | Adaptive fast-with-fallback for the SDES keepalive loop: try the fast path first and fall back to the full relay path if a fast attempt delivers no media. A per-device cache skips the fast attempt on later views once it has failed. Truthy value enables. Ignored for battery cameras, where the fast path cannot win and its short grace truncates the cold start. | unset (off) |
|
|
211
211
|
| `AIDOT_SDES_FAST_LIVEPLAY` | Don't block on the `livePlayResp` wait for eligible SDES cameras (~4.5 s faster cold start). Role-reversal models (A001064 PTZ) always excluded for correctness. **On by default**; set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
212
|
+
| `AIDOT_SDES_VIDEO_PT` | Pin the SDES offer to ONE video codec by payload type, so the camera cannot choose. The offer advertises 96 (H264) and 97 (H265) and the camera decides which to send; on an A001064 that means the same request comes back h264 1280x720 most sessions and hevc 2560x1440 occasionally, at a third of the bitrate. Set to `96` for H264 only (measured h264 720p in 4 of 4 sessions). **Do not set it to `97`** - an H265-only offer returned no video at all in 3 of 3 rounds; narrowing to H265 removes the option rather than selecting it. | unset (both offered) |
|
|
213
|
+
| `AIDOT_SDES_VIDEO_PT_ORDER` | Reorder the SDES offer's video codec list without narrowing it, as a comma-separated preference list (`97,96`). RFC 3264 makes the `m=video` payload-type list a preference list, most-preferred first, and ours has always read `96 97` - not by decision, it has simply never been varied. Whatever is named leads and the rest is appended, so this can express a preference and can never produce a narrowed or empty video m-line. **Experimental and untested on hardware:** whether the camera acts on the order is the open question, so the default is deliberately unchanged. | unset (`96 97`) |
|
|
212
214
|
| `AIDOT_DTLS_FAST_LIVEPLAY` | The DTLS (A000088) analogue: skip the `livePlayReq`-echo and `livePlayResp` waits (the dominant LAN cold-start cost) while keeping the full ICE/TURN/DTLS handshake, so remote/relay viewing is unaffected. **On by default**; set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
213
215
|
| `AIDOT_PERSISTENT_MQTT` | Reuse ONE account-level persistent MQTT connection for commands, attribute fetches, and stream-open signaling (matching the official app) instead of connecting per operation. **On by default** (live soaks cut SDES NO_MEDIA from ~57% to ~11-19%); set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
214
216
|
| `AIDOT_SERVE_RELAY` | Hold the public stream port via an internal relay that proxies to ffmpeg, so the first (cold) view connects instead of failing while ffmpeg can't pre-bind the port. Set to `0` to serve ffmpeg directly. | `1` (enabled) |
|
|
@@ -178,6 +178,8 @@ audio, idle release, the sprop cache path) are documented in
|
|
|
178
178
|
| `AIDOT_SDES_SKIP_TURN_PREALLOC` | Skip the SDES TURN relay pre-allocation (~2-3 s of cold-start latency) so signaling goes straight out with the host candidate. Faster on a LAN, at the cost of no relay fallback for a camera on a different segment / behind strict NAT. Experimental, opt-in (truthy = `1`/`true`/`yes`/`on`). | unset (off) |
|
|
179
179
|
| `AIDOT_SDES_ADAPTIVE` | Adaptive fast-with-fallback for the SDES keepalive loop: try the fast path first and fall back to the full relay path if a fast attempt delivers no media. A per-device cache skips the fast attempt on later views once it has failed. Truthy value enables. Ignored for battery cameras, where the fast path cannot win and its short grace truncates the cold start. | unset (off) |
|
|
180
180
|
| `AIDOT_SDES_FAST_LIVEPLAY` | Don't block on the `livePlayResp` wait for eligible SDES cameras (~4.5 s faster cold start). Role-reversal models (A001064 PTZ) always excluded for correctness. **On by default**; set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
181
|
+
| `AIDOT_SDES_VIDEO_PT` | Pin the SDES offer to ONE video codec by payload type, so the camera cannot choose. The offer advertises 96 (H264) and 97 (H265) and the camera decides which to send; on an A001064 that means the same request comes back h264 1280x720 most sessions and hevc 2560x1440 occasionally, at a third of the bitrate. Set to `96` for H264 only (measured h264 720p in 4 of 4 sessions). **Do not set it to `97`** - an H265-only offer returned no video at all in 3 of 3 rounds; narrowing to H265 removes the option rather than selecting it. | unset (both offered) |
|
|
182
|
+
| `AIDOT_SDES_VIDEO_PT_ORDER` | Reorder the SDES offer's video codec list without narrowing it, as a comma-separated preference list (`97,96`). RFC 3264 makes the `m=video` payload-type list a preference list, most-preferred first, and ours has always read `96 97` - not by decision, it has simply never been varied. Whatever is named leads and the rest is appended, so this can express a preference and can never produce a narrowed or empty video m-line. **Experimental and untested on hardware:** whether the camera acts on the order is the open question, so the default is deliberately unchanged. | unset (`96 97`) |
|
|
181
183
|
| `AIDOT_DTLS_FAST_LIVEPLAY` | The DTLS (A000088) analogue: skip the `livePlayReq`-echo and `livePlayResp` waits (the dominant LAN cold-start cost) while keeping the full ICE/TURN/DTLS handshake, so remote/relay viewing is unaffected. **On by default**; set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
182
184
|
| `AIDOT_PERSISTENT_MQTT` | Reuse ONE account-level persistent MQTT connection for commands, attribute fetches, and stream-open signaling (matching the official app) instead of connecting per operation. **On by default** (live soaks cut SDES NO_MEDIA from ~57% to ~11-19%); set to `0`/`false`/`no`/`off` to disable. | enabled (on) |
|
|
183
185
|
| `AIDOT_SERVE_RELAY` | Hold the public stream port via an internal relay that proxies to ffmpeg, so the first (cold) view connects instead of failing while ffmpeg can't pre-bind the port. Set to `0` to serve ffmpeg directly. | `1` (enabled) |
|
{python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/client.py
RENAMED
|
@@ -1823,13 +1823,22 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
1823
1823
|
self.device_id, len(data))
|
|
1824
1824
|
elif isinstance(body, dict):
|
|
1825
1825
|
data = body.get("data") or {}
|
|
1826
|
+
# Keys, never values: this response can carry the per-device
|
|
1827
|
+
# streaming credentials (tutkAccount / tutkPassword - see
|
|
1828
|
+
# docs/DEFERRED_FEATURES.md), and the warning below is logged
|
|
1829
|
+
# at default level, so a whole-body dump lands in every user's
|
|
1830
|
+
# home-assistant.log. The matched-device log further down
|
|
1831
|
+
# already prints keys only; these two did not.
|
|
1826
1832
|
if data:
|
|
1827
|
-
_LOGGER.debug(
|
|
1828
|
-
|
|
1833
|
+
_LOGGER.debug(
|
|
1834
|
+
"batchGetDeviceUserInfo response for %s (status=%d):"
|
|
1835
|
+
" keys=%s, %d item(s)",
|
|
1836
|
+
self.device_id, status, sorted(body.keys()), len(data),
|
|
1837
|
+
)
|
|
1829
1838
|
else:
|
|
1830
1839
|
_LOGGER.warning(
|
|
1831
|
-
"batchGetDeviceUserInfo no data for %s (status=%d):
|
|
1832
|
-
self.device_id, status, body,
|
|
1840
|
+
"batchGetDeviceUserInfo no data for %s (status=%d): keys=%s",
|
|
1841
|
+
self.device_id, status, sorted(body.keys()),
|
|
1833
1842
|
)
|
|
1834
1843
|
else:
|
|
1835
1844
|
data = {}
|
|
@@ -3468,6 +3477,22 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3468
3477
|
rtsp_push_url=self._keepalive_rtsp_url,
|
|
3469
3478
|
timeout=(_FAST_OPEN_TIMEOUT if _use_fast else 120.0),
|
|
3470
3479
|
reuse_peer_id=_loop_peer_id,
|
|
3480
|
+
# Ask for talk so THIS session can carry it. async_speak
|
|
3481
|
+
# reuses _stream_session only when it is talk-capable, and
|
|
3482
|
+
# an SDES session that did not ask never gets a return audio
|
|
3483
|
+
# track - so without this every press-to-talk opened a
|
|
3484
|
+
# second camera session alongside the live view, and the
|
|
3485
|
+
# camera holds a viewer slot for about 120 s after one ends.
|
|
3486
|
+
#
|
|
3487
|
+
# This changes the SDP offer on the path every SDES live
|
|
3488
|
+
# view takes, which is the kind of change that has caused
|
|
3489
|
+
# fleet-wide blackouts here, so it rests on evidence rather
|
|
3490
|
+
# than reasoning: the release harness has opened all seven
|
|
3491
|
+
# cameras talk-capable across three fleet runs with no
|
|
3492
|
+
# streaming regression. The flag reaches only the offer
|
|
3493
|
+
# (audio sendrecv + a=ssrc) and the shared talk state; it
|
|
3494
|
+
# does not touch the rtsp_push branch this loop uses.
|
|
3495
|
+
talk=True,
|
|
3471
3496
|
)
|
|
3472
3497
|
except asyncio.CancelledError:
|
|
3473
3498
|
self._fast_attempt_override = None
|
{python_aidot_cameras-0.17.3b2 → python_aidot_cameras-1.0.0b3}/aidot_cameras/camera/protocol.py
RENAMED
|
@@ -662,8 +662,14 @@ def _load_sprop(devid: str) -> "Optional[str]":
|
|
|
662
662
|
return None
|
|
663
663
|
|
|
664
664
|
|
|
665
|
-
def _save_sprop(devid: str, sprop: str) ->
|
|
666
|
-
"""Persist the ``sprop-parameter-sets``
|
|
665
|
+
def _save_sprop(devid: str, sprop: str) -> bool:
|
|
666
|
+
"""Persist the ``sprop-parameter-sets`` for ``devid``. True if it wrote.
|
|
667
|
+
|
|
668
|
+
Returns whether anything was actually cached, because two of the three
|
|
669
|
+
outcomes here write nothing and the caller announces the write in the only
|
|
670
|
+
log line this feature has. An unconditional announcement claims a cache
|
|
671
|
+
that an unstable camera provably does not have, which is the observable an
|
|
672
|
+
investigation reads first.
|
|
667
673
|
|
|
668
674
|
If a DIFFERENT value was already cached, this camera does not keep its
|
|
669
675
|
parameter sets stable: mark it and drop the cache, so nothing is injected
|
|
@@ -672,7 +678,7 @@ def _save_sprop(devid: str, sprop: str) -> None:
|
|
|
672
678
|
loses nothing by the injection being skipped.
|
|
673
679
|
"""
|
|
674
680
|
if _sprop_is_unstable(devid):
|
|
675
|
-
return
|
|
681
|
+
return False # already known unstable; nothing to cache for this camera
|
|
676
682
|
try:
|
|
677
683
|
prev = _load_sprop(devid)
|
|
678
684
|
if prev is not None and prev != sprop:
|
|
@@ -689,17 +695,19 @@ def _save_sprop(devid: str, sprop: str) -> None:
|
|
|
689
695
|
os.remove(_sprop_cache_path(devid))
|
|
690
696
|
except OSError:
|
|
691
697
|
pass
|
|
692
|
-
return
|
|
698
|
+
return False
|
|
693
699
|
os.makedirs(_SPROP_DIR, exist_ok=True)
|
|
694
700
|
tmp = _sprop_cache_path(devid) + ".tmp"
|
|
695
701
|
with open(tmp, "w") as fh:
|
|
696
702
|
fh.write(sprop)
|
|
697
703
|
os.replace(tmp, _sprop_cache_path(devid))
|
|
704
|
+
return True
|
|
698
705
|
except OSError as exc:
|
|
699
706
|
# Surface (don't swallow): if the cache dir isn't writable the whole
|
|
700
707
|
# sprop feature is silently inert. AIDOT_SPROP_DIR can redirect it.
|
|
701
708
|
_LOGGER.warning("sprop cache write failed (%s): %s - set AIDOT_SPROP_DIR "
|
|
702
709
|
"to a writable path", _SPROP_DIR, exc)
|
|
710
|
+
return False
|
|
703
711
|
|
|
704
712
|
|
|
705
713
|
def _inject_sprop(sdp: str, devid: str) -> str:
|
|
@@ -133,12 +133,19 @@ class SdesSession(AvioRequestMixin):
|
|
|
133
133
|
def talk_supported(self) -> bool:
|
|
134
134
|
"""True if two-way audio can be used on this SDES session.
|
|
135
135
|
|
|
136
|
-
Requires a talk-capable open (offer advertised sendrecv + a=ssrc)
|
|
137
|
-
camera audio address is filled in by
|
|
138
|
-
|
|
139
|
-
the address before emitting.
|
|
136
|
+
Requires a talk-capable open (offer advertised sendrecv + a=ssrc) AND a
|
|
137
|
+
session that is still running. The camera audio address is filled in by
|
|
138
|
+
the bridge on first inbound audio; async_start_talk tolerates being
|
|
139
|
+
called before that - the pump waits for the address before emitting.
|
|
140
|
+
|
|
141
|
+
Liveness is part of the answer because the talk state is a plain dict
|
|
142
|
+
that outlives ffmpeg, while everything that acts on it - the bridge
|
|
143
|
+
thread that sends SPEAKERSTART, the socket the pump writes to - dies
|
|
144
|
+
with ffmpeg. ``async_speak`` reuses the warm session whenever this is
|
|
145
|
+
true, so answering from the dict alone routed talk into a session that
|
|
146
|
+
could no longer carry it instead of opening a fresh one.
|
|
140
147
|
"""
|
|
141
|
-
return bool(self._talk_state)
|
|
148
|
+
return bool(self._talk_state) and self.is_alive
|
|
142
149
|
|
|
143
150
|
async def async_start_talk(
|
|
144
151
|
self, pcm_provider: "Callable[[], Optional[bytes]]"
|
|
@@ -170,10 +177,23 @@ class SdesSession(AvioRequestMixin):
|
|
|
170
177
|
target=_run_sdes_talk_pump, args=(self._talk_state,), daemon=True
|
|
171
178
|
)
|
|
172
179
|
self._talk_thread.start()
|
|
173
|
-
|
|
180
|
+
accepted = await _speaker_ack_accepted(waiter, SDES_SPEAKER_ACK_TIMEOUT_S)
|
|
181
|
+
# _speaker_ack_accepted is generous by design: it treats an unfamiliar or
|
|
182
|
+
# absent ack as acceptance, because no genuine refusal has ever been
|
|
183
|
+
# identified and guessing at one would break working cameras. That
|
|
184
|
+
# generosity is about INTERPRETING the camera's answer, and it has
|
|
185
|
+
# nothing to say about the case where the question was never asked.
|
|
186
|
+
# `speaker_on` is set on exactly one line - immediately after the bridge
|
|
187
|
+
# thread dispatches SPEAKERSTART(848) - so a falsy flag here means our
|
|
188
|
+
# own bridge never sent the command, which happens whenever ffmpeg (and
|
|
189
|
+
# with it that thread) has already exited. The ack budget above already
|
|
190
|
+
# covers SDES_SPEAKERSTART_DELAY, so a live bridge has always set it by
|
|
191
|
+
# now and this cannot race a working session.
|
|
192
|
+
if accepted and self._talk_state.get("speaker_on"):
|
|
174
193
|
return True
|
|
175
|
-
#
|
|
176
|
-
#
|
|
194
|
+
# Either the camera refused, or the speaker command never left this
|
|
195
|
+
# process. Stop the microphone rather than stream viewer audio at a
|
|
196
|
+
# speaker that never opened.
|
|
177
197
|
self._talk_state["want_speaker"] = False
|
|
178
198
|
self._talk_state["provider"] = None
|
|
179
199
|
return False
|