python-aidot-cameras 0.12.4__tar.gz → 0.12.6__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.12.4/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.6}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/client.py +16 -18
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/sdes_open.py +119 -18
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/pyproject.toml +1 -1
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6/python_aidot_cameras.egg-info}/PKG-INFO +1 -1
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/python_aidot_cameras.egg-info/SOURCES.txt +1 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_serve_audio.py +17 -18
- python_aidot_cameras-0.12.6/tests/test_sdp_payload_type_narrowing.py +83 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/LICENSE +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/MANIFEST.in +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/README.md +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/__main__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/clock.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/base.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/g711.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/g722.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/h264.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/opus.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/codecs/vpx.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/contrib/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/contrib/media.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/contrib/signaling.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/events.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/exceptions.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/jitterbuffer.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/mediastreams.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rate.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcconfiguration.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcdatachannel.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcdtlstransport.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcicetransport.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcpeerconnection.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcrtpparameters.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcrtpreceiver.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcrtpsender.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcrtptransceiver.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcsctptransport.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtcsessiondescription.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtp.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/sdp.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/stats.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/utils.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/__init__.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/constants.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/controls.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/go2rtc.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/lan_control.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/models.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/playback.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/protocol.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/sdes.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/tutk.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/webrtc.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/webrtc_open.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/client.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/const.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/credentials.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/crypto.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/device_client.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/discover.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/exceptions.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/g711.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/py.typed +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/python_aidot_cameras.egg-info/dependency_links.txt +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/python_aidot_cameras.egg-info/entry_points.txt +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/python_aidot_cameras.egg-info/requires.txt +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/python_aidot_cameras.egg-info/top_level.txt +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/setup.cfg +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_aioice_compat.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_alarm_event.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_backoff.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_deferred_hardening.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_device_login_guard.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_device_user_info_cache.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_not_ready_burst.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_pinning.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_serve_decode_skip.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_serve_open_timeout.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_skip_signaling_wait.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_slow_probe.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_egress_guard.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_go2rtc.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_go2rtc_cli.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_h264_decode_rate_limit.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_highport_nomination.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_ice_config_cache.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_keepalive_renew.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_keyframe_prompter.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_lan_control.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_light_active_color_mode.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_live_stream_param.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_logging_caps.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_motion_poll.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_credential_refetch.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_credential_self_heal.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_password_not_persisted.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_session_reconnect.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_narrow_pc_ice.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_no_undefined_names.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_offline_keepalive_pause.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_open_fail_logger.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_open_gate_delay.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_persistent_mqtt.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_persistent_mqtt_robustness.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_playback_tls.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_post_merge_hardening.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_retry_policy.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_adaptive.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_bridge_break_gate.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_echo_wait_timeout.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_fast_liveplay.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_ffmpeg_stderr_log.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_idle_release.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_open_cleanup.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_serve_cmd.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_sprop.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_talk.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_teardown_exit_log.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_watchdog.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_security_hardening.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_serializable_login_info.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_serve_relay.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_session_stats.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_speak.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_stream_cap.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_stream_drain_ownership.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_stream_idle.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_stream_teardown.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_talk.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_terminal_ack.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_token_file_robustness.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_token_refresh.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_upstream_compat.py +0 -0
- {python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_wait_or_event.py +0 -0
{python_aidot_cameras-0.12.4/python_aidot_cameras.egg-info → python_aidot_cameras-0.12.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.6
|
|
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
|
|
@@ -3360,27 +3360,25 @@ class CameraMixin(_CameraControlsMixin, _WebRTCOpenMixin, _SdesOpenMixin):
|
|
|
3360
3360
|
def _resolve_sdes_serve_audio(self) -> bool:
|
|
3361
3361
|
"""Whether to include audio in the SDES camera serve.
|
|
3362
3362
|
|
|
3363
|
-
**Default
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
``
|
|
3377
|
-
``AIDOT_SDES_SERVE_AUDIO=1`` - on a camera that does send audio steadily
|
|
3378
|
-
it works, which is why this stays an option rather than being removed."""
|
|
3363
|
+
**Default ON** for app-parity (the official app plays camera audio).
|
|
3364
|
+
|
|
3365
|
+
This was briefly defaulted off, because mapping audio used to serve no
|
|
3366
|
+
video at all: the SDP advertises two payload types per media line and
|
|
3367
|
+
ffmpeg binds the first, so the synthesised PCMA (pt 8) was discarded in
|
|
3368
|
+
favour of the advertised PCMU (pt 0) - and since the mpegts mux withholds
|
|
3369
|
+
its PAT/PMT until every mapped stream has produced a packet, the consumer
|
|
3370
|
+
got zero bytes and lost the picture too. The audio line is now narrowed to
|
|
3371
|
+
the payload type actually in use, the same way the video line already was,
|
|
3372
|
+
and the serve falls back to video-only if the audio payload type cannot be
|
|
3373
|
+
characterised in time. So audio can no longer cost the video.
|
|
3374
|
+
|
|
3375
|
+
Per-camera ``sdes_audio`` (via ``start_keepalive``) wins; else the
|
|
3376
|
+
``AIDOT_SDES_SERVE_AUDIO`` env (falsy = 0/false/no/off disables)."""
|
|
3379
3377
|
opt = getattr(self, "_sdes_audio_opt", None)
|
|
3380
3378
|
if opt is not None:
|
|
3381
3379
|
return bool(opt)
|
|
3382
|
-
return os.environ.get("AIDOT_SDES_SERVE_AUDIO", "").strip().lower() in (
|
|
3383
|
-
"
|
|
3380
|
+
return os.environ.get("AIDOT_SDES_SERVE_AUDIO", "").strip().lower() not in (
|
|
3381
|
+
"0", "false", "no", "off")
|
|
3384
3382
|
|
|
3385
3383
|
def _resolve_sdes_audio_gain_db(self) -> float:
|
|
3386
3384
|
"""Gain (dB) applied to the served SDES audio (the camera mic runs hot).
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/sdes_open.py
RENAMED
|
@@ -34,6 +34,54 @@ from .protocol import (
|
|
|
34
34
|
_LOGGER = logging.getLogger(__name__)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
# Payload types the ffmpeg SDP advertises per media line, and their alternates.
|
|
38
|
+
# The SDP offers both because the camera picks one per session.
|
|
39
|
+
_SDP_ALTERNATE_PT = {96: 97, 97: 96, 0: 8, 8: 0}
|
|
40
|
+
|
|
41
|
+
# Extra time, on top of the video payload-type wait, to characterise the audio
|
|
42
|
+
# payload type before the serve launches. Short by design: audio that has not
|
|
43
|
+
# appeared by then is served on a later open (every ffmpeg restart re-runs this
|
|
44
|
+
# wait, and by then the camera has been streaming for a while), which is cheaper
|
|
45
|
+
# than delaying the picture for every silent camera.
|
|
46
|
+
_AUDIO_PT_GRACE_S = 4.0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def narrow_sdp_payload_types(sdp_text: str, keep_video=None, keep_audio=None) -> str:
|
|
50
|
+
"""Rewrite an SDP to advertise a single payload type per media line.
|
|
51
|
+
|
|
52
|
+
ffmpeg binds each RTP depacketizer to the FIRST payload type on the m-line and
|
|
53
|
+
silently discards packets carrying any other one. The SDP written for the
|
|
54
|
+
bridge lists both candidates per line (``m=video ... 96 97`` for H.264/H.265,
|
|
55
|
+
``m=audio ... 0 8`` for PCMU/PCMA) because which one the camera uses varies per
|
|
56
|
+
session, so whichever it actually sends has to be promoted before launch.
|
|
57
|
+
|
|
58
|
+
Getting this wrong on the VIDEO line costs the picture. Getting it wrong on the
|
|
59
|
+
AUDIO line costs the picture too, and less obviously: the mpegts mux withholds
|
|
60
|
+
its PAT/PMT until every mapped stream has produced a packet, so an audio stream
|
|
61
|
+
whose packets are all discarded means the consumer receives zero bytes - video
|
|
62
|
+
included - while every other signal looks healthy.
|
|
63
|
+
|
|
64
|
+
``keep_video`` / ``keep_audio`` of None leaves that line alone.
|
|
65
|
+
"""
|
|
66
|
+
drop = {_SDP_ALTERNATE_PT[k] for k in (keep_video, keep_audio)
|
|
67
|
+
if k in _SDP_ALTERNATE_PT}
|
|
68
|
+
kept = []
|
|
69
|
+
for line in sdp_text.splitlines(keepends=True):
|
|
70
|
+
stripped = line.lstrip()
|
|
71
|
+
if stripped.startswith("m=video") and keep_video is not None:
|
|
72
|
+
line = line.replace(" 96 97", f" {keep_video}")
|
|
73
|
+
elif stripped.startswith("m=audio") and keep_audio is not None:
|
|
74
|
+
line = line.replace(" 0 8", f" {keep_audio}")
|
|
75
|
+
elif any(
|
|
76
|
+
stripped.startswith(f"a={attr}:{pt}{sep}")
|
|
77
|
+
for pt in drop
|
|
78
|
+
for attr, sep in (("rtpmap", " "), ("fmtp", " "), ("fmtp", ";"))
|
|
79
|
+
):
|
|
80
|
+
continue
|
|
81
|
+
kept.append(line)
|
|
82
|
+
return "".join(kept)
|
|
83
|
+
|
|
84
|
+
|
|
37
85
|
def _sdes_echo_wait_timeout(skip_liveplay: bool) -> float:
|
|
38
86
|
"""Seconds to block on the camera's webrtcReq echo before proceeding.
|
|
39
87
|
|
|
@@ -2141,6 +2189,11 @@ class _SdesOpenMixin:
|
|
|
2141
2189
|
# bridge on the first video RTP packet so the ffmpeg SDP can be narrowed
|
|
2142
2190
|
# to the matching single codec before ffmpeg is launched.
|
|
2143
2191
|
_first_video_pt: list = [None]
|
|
2192
|
+
# Same for audio: the ffmpeg SDP advertises BOTH PCMU (0) and PCMA (8),
|
|
2193
|
+
# and ffmpeg binds the depacketizer to the first one listed - so the line
|
|
2194
|
+
# has to be narrowed to the payload type the camera actually sends, the
|
|
2195
|
+
# same way the video line already is.
|
|
2196
|
+
_first_audio_pt: list = [None]
|
|
2144
2197
|
# Media-liveness: bridge sets [0] = time.monotonic() on every forwarded
|
|
2145
2198
|
# media packet; the keepalive watchdog reads it via SdesSession to
|
|
2146
2199
|
# restart a session the camera silently stopped feeding.
|
|
@@ -3095,10 +3148,21 @@ class _SdesOpenMixin:
|
|
|
3095
3148
|
)
|
|
3096
3149
|
if not _br_first_audio_logged:
|
|
3097
3150
|
_br_first_audio_logged = True
|
|
3151
|
+
# Record the payload type this path
|
|
3152
|
+
# SYNTHESISES (_tk_pt, PCMA) so the
|
|
3153
|
+
# SDP can be narrowed to it. Without
|
|
3154
|
+
# this the m=audio line keeps
|
|
3155
|
+
# advertising "0 8", ffmpeg binds
|
|
3156
|
+
# PCMU and discards every packet, and
|
|
3157
|
+
# the mpegts PMT is never written - so
|
|
3158
|
+
# the consumer gets zero bytes and the
|
|
3159
|
+
# VIDEO is lost with the audio.
|
|
3160
|
+
_first_audio_pt[0] = _tk_pt
|
|
3098
3161
|
_status(
|
|
3099
3162
|
f"bridge: first SDES audio -> ffmpeg"
|
|
3100
3163
|
f" loopback:{_lo_audio_port}"
|
|
3101
3164
|
f" ({len(_pd_plain)}B PCMA)"
|
|
3165
|
+
f" pt={_tk_pt}"
|
|
3102
3166
|
)
|
|
3103
3167
|
except Exception:
|
|
3104
3168
|
_LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_enc_c8_sctp', exc_info=True)
|
|
@@ -3190,6 +3254,7 @@ class _SdesOpenMixin:
|
|
|
3190
3254
|
)
|
|
3191
3255
|
if _kind == "audio" and not _br_first_audio_logged:
|
|
3192
3256
|
_br_first_audio_logged = True
|
|
3257
|
+
_first_audio_pt[0] = _pt
|
|
3193
3258
|
_status(f"bridge: first audio RTP pt={_pt}")
|
|
3194
3259
|
# Capture the camera's audio return address so the talk
|
|
3195
3260
|
# pump (SdesSession.async_start_talk) can send outbound
|
|
@@ -3454,12 +3519,13 @@ class _SdesOpenMixin:
|
|
|
3454
3519
|
if out_dir:
|
|
3455
3520
|
os.makedirs(out_dir, exist_ok=True)
|
|
3456
3521
|
# Build the ffmpeg command (single source of truth: _build_sdes_serve_cmd).
|
|
3522
|
+
_serve_audio = self._resolve_sdes_serve_audio()
|
|
3457
3523
|
cmd = _build_sdes_serve_cmd(
|
|
3458
3524
|
sdp_path=sdp_path,
|
|
3459
3525
|
rtsp_push_url=rtsp_push_url,
|
|
3460
3526
|
output_path=output_path,
|
|
3461
3527
|
max_seconds=max_seconds,
|
|
3462
|
-
sdes_audio=
|
|
3528
|
+
sdes_audio=_serve_audio,
|
|
3463
3529
|
audio_gain_db=self._resolve_sdes_audio_gain_db(),
|
|
3464
3530
|
)
|
|
3465
3531
|
# --- H.265 fix: narrow the ffmpeg SDP to the camera's actual codec ----
|
|
@@ -3470,36 +3536,71 @@ class _SdesOpenMixin:
|
|
|
3470
3536
|
# video pt, then rewrite the SDP to that single codec before launch.
|
|
3471
3537
|
# ffmpeg recovers on the next periodic keyframe, so the small spawn delay
|
|
3472
3538
|
# is harmless. Falls back to the dual-codec SDP if no video is seen.
|
|
3539
|
+
# When audio is being served the same wait has to cover the AUDIO payload
|
|
3540
|
+
# type, and for a stricter reason: an unnarrowed video line merely risks the
|
|
3541
|
+
# picture, while an unnarrowed audio line guarantees the mux never writes its
|
|
3542
|
+
# PAT/PMT, which loses the picture AND the audio. Wait for both, then take
|
|
3543
|
+
# whatever has been observed by the deadline.
|
|
3544
|
+
# Two separate budgets on purpose. Video keeps its original 15s, because
|
|
3545
|
+
# the picture is what the viewer is waiting for. Audio gets only a short
|
|
3546
|
+
# extra grace on top: sharing one deadline would make a camera that never
|
|
3547
|
+
# sends audio delay the picture from a couple of seconds to the full 15,
|
|
3548
|
+
# which trades the bug for a worse one.
|
|
3473
3549
|
_vpt_deadline = time.monotonic() + 15.0
|
|
3474
3550
|
while _first_video_pt[0] is None and time.monotonic() < _vpt_deadline:
|
|
3475
3551
|
await asyncio.sleep(0.1)
|
|
3552
|
+
if _serve_audio and _first_audio_pt[0] is None:
|
|
3553
|
+
_apt_deadline = time.monotonic() + _AUDIO_PT_GRACE_S
|
|
3554
|
+
while _first_audio_pt[0] is None and time.monotonic() < _apt_deadline:
|
|
3555
|
+
await asyncio.sleep(0.1)
|
|
3476
3556
|
_vpt = _first_video_pt[0]
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3557
|
+
_apt = _first_audio_pt[0]
|
|
3558
|
+
if _serve_audio and _apt is None:
|
|
3559
|
+
# No audio observed in the window, so the line cannot be narrowed and
|
|
3560
|
+
# mapping audio would stall the mux. Serve video only rather than
|
|
3561
|
+
# serving nothing, and say so.
|
|
3562
|
+
_LOGGER.warning(
|
|
3563
|
+
"camera %s: no audio observed before the serve launched, so the "
|
|
3564
|
+
"audio payload type is unknown; mapping it would stall the mpegts "
|
|
3565
|
+
"mux and serve no video. Continuing without audio.",
|
|
3566
|
+
getattr(self, "device_id", "?"),
|
|
3567
|
+
)
|
|
3568
|
+
_serve_audio = False
|
|
3569
|
+
cmd = _build_sdes_serve_cmd(
|
|
3570
|
+
sdp_path=sdp_path,
|
|
3571
|
+
rtsp_push_url=rtsp_push_url,
|
|
3572
|
+
output_path=output_path,
|
|
3573
|
+
max_seconds=max_seconds,
|
|
3574
|
+
sdes_audio=False,
|
|
3575
|
+
audio_gain_db=self._resolve_sdes_audio_gain_db(),
|
|
3576
|
+
)
|
|
3577
|
+
# Audio matters even more than video here. A multi-PT m-line makes ffmpeg
|
|
3578
|
+
# bind the depacketizer to the FIRST payload type and silently discard the
|
|
3579
|
+
# rest, and the mpegts mux withholds its PAT/PMT until EVERY mapped stream
|
|
3580
|
+
# has produced a packet - so an audio line advertising "0 8" (PCMU first)
|
|
3581
|
+
# on a camera that sends PCMA discards every audio packet and the consumer
|
|
3582
|
+
# receives zero bytes. That takes the video down with it, which is why
|
|
3583
|
+
# enabling serve audio appeared to break streaming outright.
|
|
3584
|
+
if _vpt in (96, 97) or (_serve_audio and _apt in (0, 8)):
|
|
3491
3585
|
def _read_sdp_file() -> str:
|
|
3492
3586
|
with open(sdp_path, encoding="utf-8") as _f_sdp:
|
|
3493
3587
|
return _f_sdp.read()
|
|
3494
3588
|
try:
|
|
3589
|
+
_keep_v = int(_vpt) if _vpt in (96, 97) else None
|
|
3590
|
+
_keep_a = int(_apt) if (_serve_audio and _apt in (0, 8)) else None
|
|
3495
3591
|
_cur_sdp = await asyncio.get_running_loop().run_in_executor(
|
|
3496
3592
|
None, _read_sdp_file)
|
|
3497
3593
|
await asyncio.get_running_loop().run_in_executor(
|
|
3498
3594
|
None, _write_text_file, sdp_path,
|
|
3499
|
-
|
|
3595
|
+
narrow_sdp_payload_types(_cur_sdp, _keep_v, _keep_a))
|
|
3500
3596
|
_status(
|
|
3501
|
-
|
|
3502
|
-
f"
|
|
3597
|
+
"SDES: narrowed ffmpeg SDP to"
|
|
3598
|
+
+ (f" video pt={_keep_v}"
|
|
3599
|
+
f" ({'H265' if _keep_v == 97 else 'H264'})"
|
|
3600
|
+
if _keep_v is not None else "")
|
|
3601
|
+
+ (f" audio pt={_keep_a}"
|
|
3602
|
+
f" ({'PCMA' if _keep_a == 8 else 'PCMU'})"
|
|
3603
|
+
if _keep_a is not None else ""))
|
|
3503
3604
|
except Exception:
|
|
3504
3605
|
_LOGGER.debug("camera %s: swallowed exception in %s", getattr(self, "device_id", "?"), '_open_sdes_stream', exc_info=True)
|
|
3505
3606
|
_LOGGER.info("SDES ffmpeg cmd: %s", " ".join(cmd))
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-aidot-cameras"
|
|
7
|
-
version = "0.12.
|
|
7
|
+
version = "0.12.6"
|
|
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"
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6/python_aidot_cameras.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-aidot-cameras
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.6
|
|
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
|
|
@@ -118,6 +118,7 @@ tests/test_sdes_sprop.py
|
|
|
118
118
|
tests/test_sdes_talk.py
|
|
119
119
|
tests/test_sdes_teardown_exit_log.py
|
|
120
120
|
tests/test_sdes_watchdog.py
|
|
121
|
+
tests/test_sdp_payload_type_narrowing.py
|
|
121
122
|
tests/test_security_hardening.py
|
|
122
123
|
tests/test_serializable_login_info.py
|
|
123
124
|
tests/test_serve_relay.py
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
"""Unit tests for _resolve_sdes_serve_audio - SDES serve audio (default
|
|
1
|
+
"""Unit tests for _resolve_sdes_serve_audio - SDES serve audio (default ON).
|
|
2
2
|
|
|
3
|
-
Locks the opt/env precedence (per-camera kwarg wins over AIDOT_SDES_SERVE_AUDIO
|
|
4
|
-
so the wiring is verifiable without a camera.
|
|
3
|
+
Locks the opt/env precedence (per-camera kwarg wins over AIDOT_SDES_SERVE_AUDIO,
|
|
4
|
+
default on for app-parity) so the wiring is verifiable without a camera.
|
|
5
5
|
|
|
6
|
-
The default
|
|
7
|
-
|
|
8
|
-
packet
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
The default was briefly OFF because mapping audio served no video at all - the
|
|
7
|
+
audio m-line advertised PCMU while the bridge sends PCMA, so ffmpeg discarded
|
|
8
|
+
every audio packet and the mpegts mux never wrote its PAT/PMT. The audio payload
|
|
9
|
+
type is narrowed now, with a video-only fallback when it cannot be characterised,
|
|
10
|
+
so audio can no longer cost the picture and the default is back on. The narrowing
|
|
11
|
+
itself is covered by test_sdp_payload_type_narrowing.py.
|
|
11
12
|
"""
|
|
12
13
|
import os
|
|
13
14
|
import sys
|
|
@@ -24,23 +25,21 @@ def _cam():
|
|
|
24
25
|
return _CAM.__new__(_CAM)
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def
|
|
28
|
+
def test_default_on_for_app_parity(monkeypatch):
|
|
28
29
|
monkeypatch.delenv("AIDOT_SDES_SERVE_AUDIO", raising=False)
|
|
29
|
-
assert _cam()._resolve_sdes_serve_audio() is
|
|
30
|
+
assert _cam()._resolve_sdes_serve_audio() is True
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
def
|
|
33
|
-
for val in ("
|
|
33
|
+
def test_env_disables(monkeypatch):
|
|
34
|
+
for val in ("0", "false", "no", "off", " Off "):
|
|
34
35
|
monkeypatch.setenv("AIDOT_SDES_SERVE_AUDIO", val)
|
|
35
|
-
assert _cam()._resolve_sdes_serve_audio() is
|
|
36
|
+
assert _cam()._resolve_sdes_serve_audio() is False, val
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def
|
|
39
|
-
|
|
40
|
-
# unrecognised value must not silently re-enable the mux-stalling path.
|
|
41
|
-
for val in ("0", "false", "no", "off", "", "anything"):
|
|
39
|
+
def test_env_truthy_or_unknown_stays_on(monkeypatch):
|
|
40
|
+
for val in ("1", "true", "yes", "on", "", "anything"):
|
|
42
41
|
monkeypatch.setenv("AIDOT_SDES_SERVE_AUDIO", val)
|
|
43
|
-
assert _cam()._resolve_sdes_serve_audio() is
|
|
42
|
+
assert _cam()._resolve_sdes_serve_audio() is True, val
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
def test_kwarg_option_wins_over_env(monkeypatch):
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""The ffmpeg SDP must advertise ONE payload type per media line.
|
|
2
|
+
|
|
3
|
+
ffmpeg binds each RTP depacketizer to the first payload type on the m-line and
|
|
4
|
+
silently discards packets carrying any other one. The SDP lists both candidates
|
|
5
|
+
per line because the camera picks one per session, so whichever it actually sends
|
|
6
|
+
has to be promoted before launch.
|
|
7
|
+
|
|
8
|
+
On the video line, getting this wrong costs the picture - that was the known
|
|
9
|
+
H.265 bug. On the audio line it also costs the picture, and far less obviously:
|
|
10
|
+
the mpegts mux withholds its PAT/PMT until every mapped stream has produced a
|
|
11
|
+
packet, so an audio stream whose packets are all discarded leaves the consumer
|
|
12
|
+
with zero bytes, video included, while signaling looks perfectly healthy. That is
|
|
13
|
+
what made enabling serve audio look like it broke streaming outright.
|
|
14
|
+
"""
|
|
15
|
+
from aidot_cameras.camera.sdes_open import narrow_sdp_payload_types
|
|
16
|
+
|
|
17
|
+
_SDP = (
|
|
18
|
+
"v=0\r\n"
|
|
19
|
+
"o=- 1 1 IN IP4 0.0.0.0\r\n"
|
|
20
|
+
"s=aidot-sdes-rx\r\n"
|
|
21
|
+
"t=0 0\r\n"
|
|
22
|
+
"m=audio 34162 RTP/AVP 0 8\r\n"
|
|
23
|
+
"c=IN IP4 127.0.0.1\r\n"
|
|
24
|
+
"a=rtpmap:0 PCMU/8000\r\n"
|
|
25
|
+
"a=rtpmap:8 PCMA/8000\r\n"
|
|
26
|
+
"m=video 38738 RTP/AVP 96 97\r\n"
|
|
27
|
+
"c=IN IP4 127.0.0.1\r\n"
|
|
28
|
+
"a=rtpmap:96 H264/90000\r\n"
|
|
29
|
+
"a=rtpmap:97 H265/90000\r\n"
|
|
30
|
+
"a=fmtp:96 packetization-mode=1\r\n"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_pcma_camera_gets_an_audio_line_advertising_only_pcma():
|
|
35
|
+
# The live failure: the camera sends PCMA (8) but PCMU (0) is listed first, so
|
|
36
|
+
# ffmpeg bound PCMU and discarded every audio packet.
|
|
37
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=96, keep_audio=8)
|
|
38
|
+
assert "m=audio 34162 RTP/AVP 8\r\n" in out
|
|
39
|
+
assert "a=rtpmap:8 PCMA/8000" in out
|
|
40
|
+
assert "a=rtpmap:0 PCMU/8000" not in out
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_pcmu_camera_gets_the_other_one():
|
|
44
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=96, keep_audio=0)
|
|
45
|
+
assert "m=audio 34162 RTP/AVP 0\r\n" in out
|
|
46
|
+
assert "a=rtpmap:0 PCMU/8000" in out
|
|
47
|
+
assert "a=rtpmap:8 PCMA/8000" not in out
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_h265_video_narrowing_still_works():
|
|
51
|
+
# The pre-existing behaviour this generalises; it must not regress.
|
|
52
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=97, keep_audio=8)
|
|
53
|
+
assert "m=video 38738 RTP/AVP 97\r\n" in out
|
|
54
|
+
assert "a=rtpmap:97 H265/90000" in out
|
|
55
|
+
assert "a=rtpmap:96 H264/90000" not in out
|
|
56
|
+
assert "a=fmtp:96 " not in out # the dropped codec's fmtp goes too
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_h264_keeps_its_fmtp():
|
|
60
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=96, keep_audio=8)
|
|
61
|
+
assert "a=fmtp:96 packetization-mode=1" in out
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_none_leaves_a_line_untouched():
|
|
65
|
+
# Video observed, audio not (or audio not being served): the audio line must
|
|
66
|
+
# be left exactly as it was rather than half-rewritten.
|
|
67
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=96, keep_audio=None)
|
|
68
|
+
assert "m=audio 34162 RTP/AVP 0 8\r\n" in out
|
|
69
|
+
assert "a=rtpmap:0 PCMU/8000" in out
|
|
70
|
+
assert "a=rtpmap:8 PCMA/8000" in out
|
|
71
|
+
assert "m=video 38738 RTP/AVP 96\r\n" in out
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_both_none_is_an_exact_passthrough():
|
|
75
|
+
assert narrow_sdp_payload_types(_SDP) == _SDP
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_line_endings_and_ordering_survive():
|
|
79
|
+
# ffmpeg is strict about SDP framing; the rewrite must not reflow it.
|
|
80
|
+
out = narrow_sdp_payload_types(_SDP, keep_video=96, keep_audio=8)
|
|
81
|
+
assert out.endswith("\r\n")
|
|
82
|
+
assert "\n\n" not in out
|
|
83
|
+
assert out.index("m=audio") < out.index("m=video") # stream order preserved
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/clock.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/rtp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/sdp.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/stats.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/_vendor/aiortc/utils.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/__init__.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/constants.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/controls.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/lan_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/playback.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/aidot_cameras/camera/webrtc_open.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_deferred_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_device_login_guard.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_device_user_info_cache.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_not_ready_burst.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_serve_decode_skip.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_serve_open_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_dtls_skip_signaling_wait.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_h264_decode_rate_limit.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_highport_nomination.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_light_active_color_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_credential_refetch.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_credential_self_heal.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_mqtt_session_reconnect.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_no_undefined_names.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_offline_keepalive_pause.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_persistent_mqtt_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_post_merge_hardening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_bridge_break_gate.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_echo_wait_timeout.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_fast_liveplay.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_ffmpeg_stderr_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_sdes_teardown_exit_log.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_security_hardening.py
RENAMED
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_serializable_login_info.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_stream_drain_ownership.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_aidot_cameras-0.12.4 → python_aidot_cameras-0.12.6}/tests/test_token_file_robustness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|