calibration: bake per-mode RC delays
This commit is contained in:
parent
40287aca33
commit
1b3b8c2cbb
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1686,7 +1686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1698,7 +1698,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -263,6 +263,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
|||||||
| `LESAVKA_TOUCHPAD_SCALE` | input routing/clipboard override |
|
| `LESAVKA_TOUCHPAD_SCALE` | input routing/clipboard override |
|
||||||
| `LESAVKA_UAC_DEV` | server hardware/device override |
|
| `LESAVKA_UAC_DEV` | server hardware/device override |
|
||||||
| `LESAVKA_UAC_SESSION_CLOCK_ALIGN` | server audio sink clock-alignment override; `0` is the host-validated default |
|
| `LESAVKA_UAC_SESSION_CLOCK_ALIGN` | server audio sink clock-alignment override; `0` is the host-validated default |
|
||||||
|
| `LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US` | server upstream per-UVC-mode UAC output-path map, e.g. `1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0` |
|
||||||
| `LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US` | server upstream output-path override; v2 uses it as the explicit UAC handoff delay relative to the shared client capture clock |
|
| `LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US` | server upstream output-path override; v2 uses it as the explicit UAC handoff delay relative to the shared client capture clock |
|
||||||
| `LESAVKA_UPSTREAM_AUDIO_MASTER_WAIT_GRACE_MS` | server upstream sync override; how long video may wait past its nominal due time for UAC audio to reach the matching timestamp, defaults to `350` |
|
| `LESAVKA_UPSTREAM_AUDIO_MASTER_WAIT_GRACE_MS` | server upstream sync override; how long video may wait past its nominal due time for UAC audio to reach the matching timestamp, defaults to `350` |
|
||||||
| `LESAVKA_UPSTREAM_BUNDLED_PLAYOUT_DELAY_MS` | compatibility alias for `LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS` |
|
| `LESAVKA_UPSTREAM_BUNDLED_PLAYOUT_DELAY_MS` | compatibility alias for `LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS` |
|
||||||
@ -274,6 +275,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
|||||||
| `LESAVKA_UPSTREAM_TIMING_TRACE` | upstream capture/rebase trace override for sync debugging |
|
| `LESAVKA_UPSTREAM_TIMING_TRACE` | upstream capture/rebase trace override for sync debugging |
|
||||||
| `LESAVKA_UPSTREAM_V2_MAX_LIVE_AGE_MS` | v2 bundled webcam freshness ceiling; bundles already older than this are dropped as one unit, defaults to `1000` |
|
| `LESAVKA_UPSTREAM_V2_MAX_LIVE_AGE_MS` | v2 bundled webcam freshness ceiling; bundles already older than this are dropped as one unit, defaults to `1000` |
|
||||||
| `LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS` | v2 optional common playout slack after sync offsets; defaults to `20` and is reduced when needed to protect the live-age budget |
|
| `LESAVKA_UPSTREAM_V2_PLAYOUT_DELAY_MS` | v2 optional common playout slack after sync offsets; defaults to `20` and is reduced when needed to protect the live-age budget |
|
||||||
|
| `LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US` | server upstream per-UVC-mode output-path map; shipped MJPEG defaults are `1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952` |
|
||||||
| `LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US` | server upstream output-path override; v2 uses it as the explicit UVC handoff delay relative to the shared client capture clock, defaults to the calibrated MJPEG/UVC offset |
|
| `LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US` | server upstream output-path override; v2 uses it as the explicit UVC handoff delay relative to the shared client capture clock, defaults to the calibrated MJPEG/UVC offset |
|
||||||
| `LESAVKA_UPLINK_CAMERA_PREVIEW` | client media capture/playback override |
|
| `LESAVKA_UPLINK_CAMERA_PREVIEW` | client media capture/playback override |
|
||||||
| `LESAVKA_UPLINK_MIC_LEVEL` | client media capture/playback override |
|
| `LESAVKA_UPLINK_MIC_LEVEL` | client media capture/playback override |
|
||||||
|
|||||||
@ -15,25 +15,68 @@ INSTALL_SERVER_BIND_ADDR=${LESAVKA_INSTALL_SERVER_BIND_ADDR:-0.0.0.0:50051}
|
|||||||
LESAVKA_TLS_DIR=${LESAVKA_TLS_DIR:-/etc/lesavka/pki}
|
LESAVKA_TLS_DIR=${LESAVKA_TLS_DIR:-/etc/lesavka/pki}
|
||||||
LESAVKA_CLIENT_BUNDLE=${LESAVKA_CLIENT_BUNDLE:-/etc/lesavka/lesavka-client-pki.tar.gz}
|
LESAVKA_CLIENT_BUNDLE=${LESAVKA_CLIENT_BUNDLE:-/etc/lesavka/lesavka-client-pki.tar.gz}
|
||||||
DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=0
|
DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=0
|
||||||
DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=170000
|
DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=135090
|
||||||
|
DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US=1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0
|
||||||
|
DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US=1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952
|
||||||
LEGACY_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=-45000
|
LEGACY_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=-45000
|
||||||
PREVIOUS_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=720000
|
PREVIOUS_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=720000
|
||||||
PREVIOUS_TUNED_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=1260000
|
PREVIOUS_TUNED_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=1260000
|
||||||
PREVIOUS_ZERO_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=0
|
PREVIOUS_ZERO_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=0
|
||||||
PREVIOUS_DELAYED_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=350000
|
PREVIOUS_DELAYED_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=350000
|
||||||
PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=130000
|
PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=130000
|
||||||
|
PREVIOUS_SCALAR_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=170000
|
||||||
PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=1090000
|
PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=1090000
|
||||||
|
|
||||||
|
lookup_mode_offset_us() {
|
||||||
|
local mode=$1
|
||||||
|
local offset_map=$2
|
||||||
|
local entry key value
|
||||||
|
IFS=',' read -r -a offset_entries <<<"${offset_map}"
|
||||||
|
for entry in "${offset_entries[@]}"; do
|
||||||
|
key=${entry%%=*}
|
||||||
|
value=${entry#*=}
|
||||||
|
if [[ "${key}" == "${mode}" && "${value}" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
printf '%s\n' "${value}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
default_uvc_mode() {
|
||||||
|
local width=${LESAVKA_UVC_WIDTH:-1280}
|
||||||
|
local height=${LESAVKA_UVC_HEIGHT:-720}
|
||||||
|
local fps=${LESAVKA_UVC_FPS:-30}
|
||||||
|
printf '%sx%s@%s\n' "${width}" "${height}" "${fps}"
|
||||||
|
}
|
||||||
|
|
||||||
|
default_mjpeg_upstream_audio_playout_offset_us() {
|
||||||
|
local mode
|
||||||
|
mode=$(default_uvc_mode)
|
||||||
|
lookup_mode_offset_us "${mode}" "$DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US" \
|
||||||
|
|| printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US"
|
||||||
|
}
|
||||||
|
|
||||||
|
default_mjpeg_upstream_video_playout_offset_us() {
|
||||||
|
local mode
|
||||||
|
mode=$(default_uvc_mode)
|
||||||
|
lookup_mode_offset_us "${mode}" "$DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US" \
|
||||||
|
|| printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US"
|
||||||
|
}
|
||||||
|
|
||||||
resolve_upstream_audio_playout_offset_us() {
|
resolve_upstream_audio_playout_offset_us() {
|
||||||
|
local default_offset_us
|
||||||
|
default_offset_us=$(default_mjpeg_upstream_audio_playout_offset_us)
|
||||||
|
|
||||||
if [[ -n ${LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} ]]; then
|
if [[ -n ${LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} ]]; then
|
||||||
printf '%s\n' "$LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$LEGACY_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_TUNED_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" ]]; then
|
if [[ ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$LEGACY_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_TUNED_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US" ]]; then
|
||||||
echo "⚠️ migrating stale upstream audio playout offset to the 0.17 freshness-first planner default." >&2
|
echo "⚠️ migrating stale upstream audio playout offset to the per-mode MJPEG/UAC default." >&2
|
||||||
echo " Use LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US to intentionally keep an older value." >&2
|
echo " Use LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US to intentionally keep an older value." >&2
|
||||||
printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$default_offset_us"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -42,19 +85,22 @@ resolve_upstream_audio_playout_offset_us() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$default_offset_us"
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_upstream_video_playout_offset_us() {
|
resolve_upstream_video_playout_offset_us() {
|
||||||
|
local default_offset_us
|
||||||
|
default_offset_us=$(default_mjpeg_upstream_video_playout_offset_us)
|
||||||
|
|
||||||
if [[ -n ${LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} ]]; then
|
if [[ -n ${LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} ]]; then
|
||||||
printf '%s\n' "$LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_ZERO_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_DELAYED_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" ]]; then
|
if [[ ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_ZERO_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_DELAYED_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_SCALAR_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" || ${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US:-} == "$PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US" ]]; then
|
||||||
echo "⚠️ migrating stale upstream video playout offset to the direct UVC/UAC MJPEG sync center." >&2
|
echo "⚠️ migrating stale upstream video playout offset to the per-mode direct UVC/UAC MJPEG sync center." >&2
|
||||||
echo " Use LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US to intentionally keep an older value." >&2
|
echo " Use LESAVKA_INSTALL_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US to intentionally keep an older value." >&2
|
||||||
printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$default_offset_us"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -63,7 +109,7 @@ resolve_upstream_video_playout_offset_us() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "$DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US"
|
printf '%s\n' "$default_offset_us"
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest_package_version() {
|
manifest_package_version() {
|
||||||
@ -1004,6 +1050,8 @@ fi
|
|||||||
printf 'LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS=%s\n' "${LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS:-350}"
|
printf 'LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS=%s\n' "${LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS:-350}"
|
||||||
printf 'LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS=%s\n' "${LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS:-1000}"
|
printf 'LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS=%s\n' "${LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS:-1000}"
|
||||||
printf 'LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS=%s\n' "${LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS:-60000}"
|
printf 'LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS=%s\n' "${LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS:-60000}"
|
||||||
|
printf 'LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US=%s\n' "${LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US:-$DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US}"
|
||||||
|
printf 'LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US=%s\n' "${LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US:-$DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US}"
|
||||||
printf 'LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=%s\n' "$(resolve_upstream_audio_playout_offset_us)"
|
printf 'LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=%s\n' "$(resolve_upstream_audio_playout_offset_us)"
|
||||||
printf 'LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=%s\n' "$(resolve_upstream_video_playout_offset_us)"
|
printf 'LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=%s\n' "$(resolve_upstream_video_playout_offset_us)"
|
||||||
printf 'LESAVKA_UPSTREAM_PAIR_SLACK_US=%s\n' "${LESAVKA_UPSTREAM_PAIR_SLACK_US:-80000}"
|
printf 'LESAVKA_UPSTREAM_PAIR_SLACK_US=%s\n' "${LESAVKA_UPSTREAM_PAIR_SLACK_US:-80000}"
|
||||||
|
|||||||
@ -32,9 +32,9 @@ SSH_OPTS=${SSH_OPTS:-"-o BatchMode=yes -o ConnectTimeout=30"}
|
|||||||
LESAVKA_SERVER_RC_CORE_WEBCAM_MODES=${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES:-1280x720@20,1280x720@30,1920x1080@20,1920x1080@30}
|
LESAVKA_SERVER_RC_CORE_WEBCAM_MODES=${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES:-1280x720@20,1280x720@30,1920x1080@20,1920x1080@30}
|
||||||
LESAVKA_SERVER_RC_MODES=${LESAVKA_SERVER_RC_MODES:-${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES}}
|
LESAVKA_SERVER_RC_MODES=${LESAVKA_SERVER_RC_MODES:-${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES}}
|
||||||
LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US:-${LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US:-0}}
|
LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US:-${LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US:-0}}
|
||||||
LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US:-170000}
|
LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US:-135090}
|
||||||
LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US:-1280x720@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1280x720@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US}}
|
LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US:-1280x720@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1280x720@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US}}
|
||||||
LESAVKA_SERVER_RC_MODE_DELAYS_US=${LESAVKA_SERVER_RC_MODE_DELAYS_US:-1280x720@20=170000,1280x720@30=170000,1920x1080@20=170000,1920x1080@30=170000}
|
LESAVKA_SERVER_RC_MODE_DELAYS_US=${LESAVKA_SERVER_RC_MODE_DELAYS_US:-1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952}
|
||||||
LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES=${LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES:-1280x720,1920x1080}
|
LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES=${LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES:-1280x720,1920x1080}
|
||||||
LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS=${LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS:-20,30}
|
LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS=${LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS:-20,30}
|
||||||
LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX:-Logitech|BRIO|C9[0-9]+|HD UVC WebCam|USB2[.]0 HD|Integrated Camera|Webcam|Camera}
|
LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX:-Logitech|BRIO|C9[0-9]+|HD UVC WebCam|USB2[.]0 HD|Integrated Camera|Webcam|Camera}
|
||||||
@ -979,6 +979,8 @@ reconfigure_server_mode() {
|
|||||||
local width=$2
|
local width=$2
|
||||||
local height=$3
|
local height=$3
|
||||||
local fps=$4
|
local fps=$4
|
||||||
|
local audio_delay_us=${5:-$(lookup_audio_delay_us "${mode}")}
|
||||||
|
local video_delay_us=${6:-$(lookup_video_delay_us "${mode}")}
|
||||||
[[ "${LESAVKA_SERVER_RC_RECONFIGURE}" != "0" ]] || return 0
|
[[ "${LESAVKA_SERVER_RC_RECONFIGURE}" != "0" ]] || return 0
|
||||||
|
|
||||||
echo "==> reconfiguring ${LESAVKA_SERVER_HOST} UVC gadget for ${mode}"
|
echo "==> reconfiguring ${LESAVKA_SERVER_HOST} UVC gadget for ${mode}"
|
||||||
@ -987,6 +989,8 @@ reconfigure_server_mode() {
|
|||||||
LESAVKA_UVC_WIDTH="${width}" \
|
LESAVKA_UVC_WIDTH="${width}" \
|
||||||
LESAVKA_UVC_HEIGHT="${height}" \
|
LESAVKA_UVC_HEIGHT="${height}" \
|
||||||
LESAVKA_UVC_FPS="${fps}" \
|
LESAVKA_UVC_FPS="${fps}" \
|
||||||
|
LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US="${audio_delay_us}" \
|
||||||
|
LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US="${video_delay_us}" \
|
||||||
bash -c "${LESAVKA_SERVER_RC_RECONFIGURE_COMMAND}"
|
bash -c "${LESAVKA_SERVER_RC_RECONFIGURE_COMMAND}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@ -1009,7 +1013,11 @@ reconfigure_server_mode() {
|
|||||||
"${LESAVKA_SERVER_RC_ALLOW_GADGET_RESET}" \
|
"${LESAVKA_SERVER_RC_ALLOW_GADGET_RESET}" \
|
||||||
"${LESAVKA_SERVER_RC_FORCE_GADGET_REBUILD}" \
|
"${LESAVKA_SERVER_RC_FORCE_GADGET_REBUILD}" \
|
||||||
"${LESAVKA_SERVER_RC_RECONFIGURE_SETTLE_SECONDS}" \
|
"${LESAVKA_SERVER_RC_RECONFIGURE_SETTLE_SECONDS}" \
|
||||||
"${LESAVKA_SERVER_RC_RECONFIGURE_VERBOSE}" <<'REMOTE_RECONFIGURE'
|
"${LESAVKA_SERVER_RC_RECONFIGURE_VERBOSE}" \
|
||||||
|
"${audio_delay_us}" \
|
||||||
|
"${video_delay_us}" \
|
||||||
|
"${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US}" \
|
||||||
|
"${LESAVKA_SERVER_RC_MODE_DELAYS_US}" <<'REMOTE_RECONFIGURE'
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
mode=$1
|
mode=$1
|
||||||
width=$2
|
width=$2
|
||||||
@ -1021,6 +1029,10 @@ allow_gadget_reset=$7
|
|||||||
force_gadget_rebuild=$8
|
force_gadget_rebuild=$8
|
||||||
settle_seconds=$9
|
settle_seconds=$9
|
||||||
verbose=${10}
|
verbose=${10}
|
||||||
|
audio_delay_us=${11}
|
||||||
|
video_delay_us=${12}
|
||||||
|
audio_delay_map=${13}
|
||||||
|
video_delay_map=${14}
|
||||||
|
|
||||||
set_env_value() {
|
set_env_value() {
|
||||||
local file=$1
|
local file=$1
|
||||||
@ -1068,6 +1080,10 @@ set_env_value /etc/lesavka/server.env LESAVKA_UVC_WIDTH "${width}"
|
|||||||
set_env_value /etc/lesavka/server.env LESAVKA_UVC_HEIGHT "${height}"
|
set_env_value /etc/lesavka/server.env LESAVKA_UVC_HEIGHT "${height}"
|
||||||
set_env_value /etc/lesavka/server.env LESAVKA_UVC_FPS "${fps}"
|
set_env_value /etc/lesavka/server.env LESAVKA_UVC_FPS "${fps}"
|
||||||
set_env_value /etc/lesavka/server.env LESAVKA_UVC_INTERVAL "${interval}"
|
set_env_value /etc/lesavka/server.env LESAVKA_UVC_INTERVAL "${interval}"
|
||||||
|
set_env_value /etc/lesavka/server.env LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US "${audio_delay_map}"
|
||||||
|
set_env_value /etc/lesavka/server.env LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US "${video_delay_map}"
|
||||||
|
set_env_value /etc/lesavka/server.env LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US "${audio_delay_us}"
|
||||||
|
set_env_value /etc/lesavka/server.env LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US "${video_delay_us}"
|
||||||
|
|
||||||
set_env_value /etc/lesavka/uvc.env LESAVKA_UVC_CODEC "${codec}"
|
set_env_value /etc/lesavka/uvc.env LESAVKA_UVC_CODEC "${codec}"
|
||||||
set_env_value /etc/lesavka/uvc.env LESAVKA_UVC_WIDTH "${width}"
|
set_env_value /etc/lesavka/uvc.env LESAVKA_UVC_WIDTH "${width}"
|
||||||
@ -2282,7 +2298,7 @@ for mode in "${modes[@]}"; do
|
|||||||
mkdir -p "${mode_dir}"
|
mkdir -p "${mode_dir}"
|
||||||
|
|
||||||
echo "==> mode ${mode} run ${mode_run_index} repeat ${repeat_index}/${LESAVKA_SERVER_RC_REPEAT_COUNT}: video_delay_us=${video_delay_us} audio_delay_us=${audio_delay_us}"
|
echo "==> mode ${mode} run ${mode_run_index} repeat ${repeat_index}/${LESAVKA_SERVER_RC_REPEAT_COUNT}: video_delay_us=${video_delay_us} audio_delay_us=${audio_delay_us}"
|
||||||
reconfigure_server_mode "${mode}" "${width}" "${height}" "${fps}"
|
reconfigure_server_mode "${mode}" "${width}" "${height}" "${fps}" "${audio_delay_us}" "${video_delay_us}"
|
||||||
wait_tethys_media_ready "${mode}" "${width}" "${height}" "${fps}"
|
wait_tethys_media_ready "${mode}" "${width}" "${height}" "${fps}"
|
||||||
|
|
||||||
if [[ "${LESAVKA_SERVER_RC_SIGNAL_READY}" != "0" && "${LESAVKA_SERVER_RC_SIGNAL_READY_MODE}" != "separate" ]]; then
|
if [[ "${LESAVKA_SERVER_RC_SIGNAL_READY}" != "0" && "${LESAVKA_SERVER_RC_SIGNAL_READY_MODE}" != "separate" ]]; then
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.19.28"
|
version = "0.19.29"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -10,16 +10,25 @@ use lesavka_common::lesavka::{
|
|||||||
use crate::upstream_media_runtime::UpstreamMediaRuntime;
|
use crate::upstream_media_runtime::UpstreamMediaRuntime;
|
||||||
|
|
||||||
pub const FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 0;
|
pub const FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 0;
|
||||||
// Direct UVC/UAC output-delay probes against the lab RC target put the
|
pub const FACTORY_MJPEG_VIDEO_OFFSET_1280X720_20_US: i64 = 162_659;
|
||||||
// server-to-target sync center near 170ms for MJPEG/UVC video. This is an
|
pub const FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US: i64 = 135_090;
|
||||||
// output-path compensation, not a freshness buffer.
|
pub const FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_20_US: i64 = 160_045;
|
||||||
pub const FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 170_000;
|
pub const FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US: i64 = 127_952;
|
||||||
|
pub const FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US: &str =
|
||||||
|
"1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0";
|
||||||
|
pub const FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US: &str =
|
||||||
|
"1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952";
|
||||||
|
// Direct UVC/UAC output-delay probes against the lab RC target showed a
|
||||||
|
// per-mode sync center for MJPEG/UVC video. This is output-path compensation,
|
||||||
|
// not a freshness buffer. The scalar fallback follows the default UVC mode.
|
||||||
|
pub const FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US;
|
||||||
const LEGACY_FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = -45_000;
|
const LEGACY_FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = -45_000;
|
||||||
const PREVIOUS_FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 720_000;
|
const PREVIOUS_FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 720_000;
|
||||||
const PREVIOUS_TUNED_MJPEG_AUDIO_OFFSET_US: i64 = 1_260_000;
|
const PREVIOUS_TUNED_MJPEG_AUDIO_OFFSET_US: i64 = 1_260_000;
|
||||||
const PREVIOUS_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 0;
|
const PREVIOUS_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 0;
|
||||||
const PREVIOUS_DELAYED_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 350_000;
|
const PREVIOUS_DELAYED_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 350_000;
|
||||||
const PREVIOUS_BROWSER_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 130_000;
|
const PREVIOUS_BROWSER_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 130_000;
|
||||||
|
const PREVIOUS_SCALAR_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 170_000;
|
||||||
const PREVIOUS_OVERSHOT_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 1_090_000;
|
const PREVIOUS_OVERSHOT_FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 1_090_000;
|
||||||
const PROFILE: &str = "mjpeg";
|
const PROFILE: &str = "mjpeg";
|
||||||
const FACTORY_CONFIDENCE: &str = "factory";
|
const FACTORY_CONFIDENCE: &str = "factory";
|
||||||
@ -208,10 +217,29 @@ pub fn calibration_path() -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot_from_env() -> CalibrationSnapshot {
|
fn snapshot_from_env() -> CalibrationSnapshot {
|
||||||
let env_audio = env_i64("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US");
|
let mode = current_uvc_mode();
|
||||||
let env_video = env_i64("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US");
|
let factory_audio_offset_us = mode
|
||||||
let default_audio_offset_us = env_audio.unwrap_or(FACTORY_MJPEG_AUDIO_OFFSET_US);
|
.as_deref()
|
||||||
let default_video_offset_us = env_video.unwrap_or(FACTORY_MJPEG_VIDEO_OFFSET_US);
|
.and_then(|mode| lookup_mode_offset_us(FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US, mode))
|
||||||
|
.unwrap_or(FACTORY_MJPEG_AUDIO_OFFSET_US);
|
||||||
|
let factory_video_offset_us = mode
|
||||||
|
.as_deref()
|
||||||
|
.and_then(|mode| lookup_mode_offset_us(FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US, mode))
|
||||||
|
.unwrap_or(FACTORY_MJPEG_VIDEO_OFFSET_US);
|
||||||
|
let env_audio = configured_offset_us(
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US",
|
||||||
|
mode.as_deref(),
|
||||||
|
is_stale_audio_offset_us,
|
||||||
|
);
|
||||||
|
let env_video = configured_offset_us(
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US",
|
||||||
|
mode.as_deref(),
|
||||||
|
is_stale_video_offset_us,
|
||||||
|
);
|
||||||
|
let default_audio_offset_us = env_audio.unwrap_or(factory_audio_offset_us);
|
||||||
|
let default_video_offset_us = env_video.unwrap_or(factory_video_offset_us);
|
||||||
let source = if env_audio.is_some() || env_video.is_some() {
|
let source = if env_audio.is_some() || env_video.is_some() {
|
||||||
"env".to_string()
|
"env".to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -224,8 +252,8 @@ fn snapshot_from_env() -> CalibrationSnapshot {
|
|||||||
};
|
};
|
||||||
CalibrationSnapshot {
|
CalibrationSnapshot {
|
||||||
profile: PROFILE.to_string(),
|
profile: PROFILE.to_string(),
|
||||||
factory_audio_offset_us: FACTORY_MJPEG_AUDIO_OFFSET_US,
|
factory_audio_offset_us,
|
||||||
factory_video_offset_us: FACTORY_MJPEG_VIDEO_OFFSET_US,
|
factory_video_offset_us,
|
||||||
default_audio_offset_us,
|
default_audio_offset_us,
|
||||||
default_video_offset_us,
|
default_video_offset_us,
|
||||||
active_audio_offset_us: default_audio_offset_us,
|
active_audio_offset_us: default_audio_offset_us,
|
||||||
@ -254,8 +282,8 @@ fn parse_snapshot(raw: &str) -> CalibrationSnapshot {
|
|||||||
};
|
};
|
||||||
CalibrationSnapshot {
|
CalibrationSnapshot {
|
||||||
profile: value("profile").unwrap_or(fallback.profile),
|
profile: value("profile").unwrap_or(fallback.profile),
|
||||||
factory_audio_offset_us: FACTORY_MJPEG_AUDIO_OFFSET_US,
|
factory_audio_offset_us: fallback.factory_audio_offset_us,
|
||||||
factory_video_offset_us: FACTORY_MJPEG_VIDEO_OFFSET_US,
|
factory_video_offset_us: fallback.factory_video_offset_us,
|
||||||
default_audio_offset_us: number(
|
default_audio_offset_us: number(
|
||||||
"default_audio_offset_us",
|
"default_audio_offset_us",
|
||||||
fallback.default_audio_offset_us,
|
fallback.default_audio_offset_us,
|
||||||
@ -282,21 +310,12 @@ fn migrate_legacy_snapshot(mut state: CalibrationSnapshot) -> CalibrationSnapsho
|
|||||||
) && state
|
) && state
|
||||||
.detail
|
.detail
|
||||||
.contains("loaded upstream A/V calibration defaults");
|
.contains("loaded upstream A/V calibration defaults");
|
||||||
let untouched_legacy_audio = (matches!(
|
let untouched_legacy_audio = (is_stale_audio_offset_us(state.default_audio_offset_us)
|
||||||
state.default_audio_offset_us,
|
|| state.default_audio_offset_us == state.factory_audio_offset_us
|
||||||
FACTORY_MJPEG_AUDIO_OFFSET_US
|
|| clamped_previous_baseline)
|
||||||
| LEGACY_FACTORY_MJPEG_AUDIO_OFFSET_US
|
|
||||||
| PREVIOUS_FACTORY_MJPEG_AUDIO_OFFSET_US
|
|
||||||
| PREVIOUS_TUNED_MJPEG_AUDIO_OFFSET_US
|
|
||||||
) || clamped_previous_baseline)
|
|
||||||
&& state.active_audio_offset_us == state.default_audio_offset_us;
|
&& state.active_audio_offset_us == state.default_audio_offset_us;
|
||||||
let untouched_legacy_video = matches!(
|
let untouched_legacy_video = is_stale_video_offset_us(state.default_video_offset_us)
|
||||||
state.default_video_offset_us,
|
&& state.active_video_offset_us == state.default_video_offset_us;
|
||||||
PREVIOUS_FACTORY_MJPEG_VIDEO_OFFSET_US
|
|
||||||
| PREVIOUS_DELAYED_FACTORY_MJPEG_VIDEO_OFFSET_US
|
|
||||||
| PREVIOUS_BROWSER_FACTORY_MJPEG_VIDEO_OFFSET_US
|
|
||||||
| PREVIOUS_OVERSHOT_FACTORY_MJPEG_VIDEO_OFFSET_US
|
|
||||||
) && state.active_video_offset_us == state.default_video_offset_us;
|
|
||||||
if state.profile == PROFILE
|
if state.profile == PROFILE
|
||||||
&& source_allows_migration
|
&& source_allows_migration
|
||||||
&& confidence_allows_migration
|
&& confidence_allows_migration
|
||||||
@ -305,18 +324,18 @@ fn migrate_legacy_snapshot(mut state: CalibrationSnapshot) -> CalibrationSnapsho
|
|||||||
{
|
{
|
||||||
let old_audio_offset_us = state.default_audio_offset_us;
|
let old_audio_offset_us = state.default_audio_offset_us;
|
||||||
let old_video_offset_us = state.default_video_offset_us;
|
let old_video_offset_us = state.default_video_offset_us;
|
||||||
state.default_audio_offset_us = FACTORY_MJPEG_AUDIO_OFFSET_US;
|
state.default_audio_offset_us = state.factory_audio_offset_us;
|
||||||
state.active_audio_offset_us = FACTORY_MJPEG_AUDIO_OFFSET_US;
|
state.active_audio_offset_us = state.factory_audio_offset_us;
|
||||||
state.default_video_offset_us = FACTORY_MJPEG_VIDEO_OFFSET_US;
|
state.default_video_offset_us = state.factory_video_offset_us;
|
||||||
state.active_video_offset_us = FACTORY_MJPEG_VIDEO_OFFSET_US;
|
state.active_video_offset_us = state.factory_video_offset_us;
|
||||||
state.source = "factory".to_string();
|
state.source = "factory".to_string();
|
||||||
state.confidence = FACTORY_CONFIDENCE.to_string();
|
state.confidence = FACTORY_CONFIDENCE.to_string();
|
||||||
state.detail = format!(
|
state.detail = format!(
|
||||||
"migrated legacy MJPEG upstream A/V baseline from audio {:+.1}ms/video {:+.1}ms to audio {:+.1}ms/video {:+.1}ms",
|
"migrated legacy MJPEG upstream A/V baseline from audio {:+.1}ms/video {:+.1}ms to audio {:+.1}ms/video {:+.1}ms",
|
||||||
old_audio_offset_us as f64 / 1000.0,
|
old_audio_offset_us as f64 / 1000.0,
|
||||||
old_video_offset_us as f64 / 1000.0,
|
old_video_offset_us as f64 / 1000.0,
|
||||||
FACTORY_MJPEG_AUDIO_OFFSET_US as f64 / 1000.0,
|
state.factory_audio_offset_us as f64 / 1000.0,
|
||||||
FACTORY_MJPEG_VIDEO_OFFSET_US as f64 / 1000.0
|
state.factory_video_offset_us as f64 / 1000.0
|
||||||
);
|
);
|
||||||
touch(&mut state);
|
touch(&mut state);
|
||||||
}
|
}
|
||||||
@ -351,6 +370,67 @@ fn touch(state: &mut CalibrationSnapshot) {
|
|||||||
state.updated_at = Utc::now().to_rfc3339();
|
state.updated_at = Utc::now().to_rfc3339();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configured_offset_us(
|
||||||
|
mode_map_name: &str,
|
||||||
|
scalar_name: &str,
|
||||||
|
mode: Option<&str>,
|
||||||
|
is_stale_scalar: impl Fn(i64) -> bool,
|
||||||
|
) -> Option<i64> {
|
||||||
|
mode.and_then(|mode| env_mode_offset_us(mode_map_name, mode))
|
||||||
|
.or_else(|| env_i64(scalar_name).filter(|offset| !is_stale_scalar(*offset)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_uvc_mode() -> Option<String> {
|
||||||
|
env_mode("UVC_MODE")
|
||||||
|
.or_else(|| env_mode("LESAVKA_UVC_MODE"))
|
||||||
|
.or_else(|| {
|
||||||
|
let width = env_u32("LESAVKA_UVC_WIDTH")?;
|
||||||
|
let height = env_u32("LESAVKA_UVC_HEIGHT")?;
|
||||||
|
let fps = env_u32("LESAVKA_UVC_FPS")
|
||||||
|
.or_else(|| {
|
||||||
|
env_u32("LESAVKA_UVC_INTERVAL")
|
||||||
|
.and_then(|interval| (interval > 0).then_some(10_000_000 / interval))
|
||||||
|
})?
|
||||||
|
.max(1);
|
||||||
|
Some(format!("{width}x{height}@{fps}"))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
let width = env_u32("LESAVKA_CAM_WIDTH")?;
|
||||||
|
let height = env_u32("LESAVKA_CAM_HEIGHT")?;
|
||||||
|
let fps = env_u32("LESAVKA_CAM_FPS")?.max(1);
|
||||||
|
Some(format!("{width}x{height}@{fps}"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_mode(name: &str) -> Option<String> {
|
||||||
|
std::env::var(name).ok().and_then(|value| {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
let valid = trimmed.split_once('@').and_then(|(size, fps)| {
|
||||||
|
let (width, height) = size.split_once('x')?;
|
||||||
|
width.parse::<u32>().ok()?;
|
||||||
|
height.parse::<u32>().ok()?;
|
||||||
|
fps.parse::<u32>().ok()?;
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
valid.map(|()| trimmed.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_mode_offset_us(name: &str, mode: &str) -> Option<i64> {
|
||||||
|
std::env::var(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|map| lookup_mode_offset_us(&map, mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_mode_offset_us(map: &str, mode: &str) -> Option<i64> {
|
||||||
|
map.split(',').find_map(|entry| {
|
||||||
|
let (key, value) = entry.trim().split_once('=')?;
|
||||||
|
(key.trim() == mode)
|
||||||
|
.then(|| value.trim().parse::<i64>().ok().map(clamp_offset))
|
||||||
|
.flatten()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn env_i64(name: &str) -> Option<i64> {
|
fn env_i64(name: &str) -> Option<i64> {
|
||||||
std::env::var(name)
|
std::env::var(name)
|
||||||
.ok()
|
.ok()
|
||||||
@ -358,6 +438,32 @@ fn env_i64(name: &str) -> Option<i64> {
|
|||||||
.map(clamp_offset)
|
.map(clamp_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn env_u32(name: &str) -> Option<u32> {
|
||||||
|
std::env::var(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|value| value.trim().parse::<u32>().ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_stale_audio_offset_us(offset: i64) -> bool {
|
||||||
|
matches!(
|
||||||
|
offset,
|
||||||
|
LEGACY_FACTORY_MJPEG_AUDIO_OFFSET_US
|
||||||
|
| PREVIOUS_FACTORY_MJPEG_AUDIO_OFFSET_US
|
||||||
|
| PREVIOUS_TUNED_MJPEG_AUDIO_OFFSET_US
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_stale_video_offset_us(offset: i64) -> bool {
|
||||||
|
matches!(
|
||||||
|
offset,
|
||||||
|
PREVIOUS_FACTORY_MJPEG_VIDEO_OFFSET_US
|
||||||
|
| PREVIOUS_DELAYED_FACTORY_MJPEG_VIDEO_OFFSET_US
|
||||||
|
| PREVIOUS_BROWSER_FACTORY_MJPEG_VIDEO_OFFSET_US
|
||||||
|
| PREVIOUS_SCALAR_FACTORY_MJPEG_VIDEO_OFFSET_US
|
||||||
|
| PREVIOUS_OVERSHOT_FACTORY_MJPEG_VIDEO_OFFSET_US
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn clamp_offset(value: i64) -> i64 {
|
fn clamp_offset(value: i64) -> i64 {
|
||||||
value.clamp(-OFFSET_LIMIT_US, OFFSET_LIMIT_US)
|
value.clamp(-OFFSET_LIMIT_US, OFFSET_LIMIT_US)
|
||||||
}
|
}
|
||||||
@ -377,6 +483,18 @@ mod tests {
|
|||||||
[
|
[
|
||||||
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
("LESAVKA_UVC_WIDTH", None::<&str>),
|
||||||
|
("LESAVKA_UVC_HEIGHT", None::<&str>),
|
||||||
|
("LESAVKA_UVC_FPS", None::<&str>),
|
||||||
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
let state = snapshot_from_env();
|
let state = snapshot_from_env();
|
||||||
@ -387,6 +505,98 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_snapshot_uses_uvc_mode_factory_calibration() {
|
||||||
|
temp_env::with_vars(
|
||||||
|
[
|
||||||
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
("LESAVKA_UVC_WIDTH", Some("1920")),
|
||||||
|
("LESAVKA_UVC_HEIGHT", Some("1080")),
|
||||||
|
("LESAVKA_UVC_FPS", Some("30")),
|
||||||
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let state = snapshot_from_env();
|
||||||
|
assert_eq!(
|
||||||
|
state.default_video_offset_us,
|
||||||
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.factory_video_offset_us,
|
||||||
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US
|
||||||
|
);
|
||||||
|
assert_eq!(state.source, "factory");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mode_offset_map_overrides_stale_scalar_offset() {
|
||||||
|
temp_env::with_vars(
|
||||||
|
[
|
||||||
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", Some("170000")),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
Some("1280x720@20=123456"),
|
||||||
|
),
|
||||||
|
("LESAVKA_UVC_WIDTH", Some("1280")),
|
||||||
|
("LESAVKA_UVC_HEIGHT", Some("720")),
|
||||||
|
("LESAVKA_UVC_FPS", Some("20")),
|
||||||
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let state = snapshot_from_env();
|
||||||
|
assert_eq!(state.default_video_offset_us, 123_456);
|
||||||
|
assert_eq!(state.source, "env");
|
||||||
|
assert_eq!(state.confidence, "configured");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stale_scalar_video_offset_falls_back_to_mode_factory() {
|
||||||
|
temp_env::with_vars(
|
||||||
|
[
|
||||||
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
||||||
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", Some("170000")),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
None::<&str>,
|
||||||
|
),
|
||||||
|
("LESAVKA_UVC_WIDTH", Some("1920")),
|
||||||
|
("LESAVKA_UVC_HEIGHT", Some("1080")),
|
||||||
|
("LESAVKA_UVC_FPS", Some("20")),
|
||||||
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
let state = snapshot_from_env();
|
||||||
|
assert_eq!(
|
||||||
|
state.default_video_offset_us,
|
||||||
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_20_US
|
||||||
|
);
|
||||||
|
assert_eq!(state.source, "factory");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn store_persists_manual_adjustments_and_updates_runtime() {
|
fn store_persists_manual_adjustments_and_updates_runtime() {
|
||||||
let file = NamedTempFile::new().expect("temp calibration file");
|
let file = NamedTempFile::new().expect("temp calibration file");
|
||||||
@ -535,7 +745,7 @@ mod tests {
|
|||||||
runtime.playout_offsets(),
|
runtime.playout_offsets(),
|
||||||
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
||||||
);
|
);
|
||||||
assert!(state.detail.contains("to audio +0.0ms/video +170.0ms"));
|
assert!(state.detail.contains("to audio +0.0ms/video +135.1ms"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,9 +7,12 @@ use std::time::Duration;
|
|||||||
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
|
|
||||||
|
use crate::calibration::{
|
||||||
|
FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US, FACTORY_MJPEG_AUDIO_OFFSET_US,
|
||||||
|
FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US, FACTORY_MJPEG_VIDEO_OFFSET_US,
|
||||||
|
};
|
||||||
|
|
||||||
const TIMING_WINDOW_CAPACITY: usize = 240;
|
const TIMING_WINDOW_CAPACITY: usize = 240;
|
||||||
const FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 0;
|
|
||||||
const FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = 1_090_000;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum UpstreamMediaKind {
|
pub enum UpstreamMediaKind {
|
||||||
@ -727,17 +730,68 @@ fn upstream_playout_delay() -> Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn playout_offset_us(kind: UpstreamMediaKind) -> i64 {
|
fn playout_offset_us(kind: UpstreamMediaKind) -> i64 {
|
||||||
let name = match kind {
|
let (scalar_name, mode_map_name, factory_map, factory_offset_us) = match kind {
|
||||||
UpstreamMediaKind::Camera => "LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US",
|
UpstreamMediaKind::Camera => (
|
||||||
UpstreamMediaKind::Microphone => "LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US",
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US",
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US,
|
||||||
|
FACTORY_MJPEG_VIDEO_OFFSET_US,
|
||||||
|
),
|
||||||
|
UpstreamMediaKind::Microphone => (
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US",
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
||||||
|
FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US,
|
||||||
|
FACTORY_MJPEG_AUDIO_OFFSET_US,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
let mode = current_uvc_mode();
|
||||||
|
mode.as_deref()
|
||||||
|
.and_then(|mode| env_mode_offset_us(mode_map_name, mode))
|
||||||
|
.or_else(|| env_i64(scalar_name))
|
||||||
|
.or_else(|| {
|
||||||
|
mode.as_deref()
|
||||||
|
.and_then(|mode| lookup_mode_offset_us(factory_map, mode))
|
||||||
|
})
|
||||||
|
.unwrap_or(factory_offset_us)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_uvc_mode() -> Option<String> {
|
||||||
|
let width = env_u32("LESAVKA_UVC_WIDTH")?;
|
||||||
|
let height = env_u32("LESAVKA_UVC_HEIGHT")?;
|
||||||
|
let fps = env_u32("LESAVKA_UVC_FPS")
|
||||||
|
.or_else(|| {
|
||||||
|
env_u32("LESAVKA_UVC_INTERVAL")
|
||||||
|
.and_then(|interval| (interval > 0).then_some(10_000_000 / interval))
|
||||||
|
})?
|
||||||
|
.max(1);
|
||||||
|
Some(format!("{width}x{height}@{fps}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_mode_offset_us(name: &str, mode: &str) -> Option<i64> {
|
||||||
|
std::env::var(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|map| lookup_mode_offset_us(&map, mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_mode_offset_us(map: &str, mode: &str) -> Option<i64> {
|
||||||
|
map.split(',').find_map(|entry| {
|
||||||
|
let (key, value) = entry.trim().split_once('=')?;
|
||||||
|
(key.trim() == mode)
|
||||||
|
.then(|| value.trim().parse::<i64>().ok())
|
||||||
|
.flatten()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_i64(name: &str) -> Option<i64> {
|
||||||
std::env::var(name)
|
std::env::var(name)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|value| value.trim().parse::<i64>().ok())
|
.and_then(|value| value.trim().parse::<i64>().ok())
|
||||||
.unwrap_or(match kind {
|
}
|
||||||
UpstreamMediaKind::Camera => FACTORY_MJPEG_VIDEO_OFFSET_US,
|
|
||||||
UpstreamMediaKind::Microphone => FACTORY_MJPEG_AUDIO_OFFSET_US,
|
fn env_u32(name: &str) -> Option<u32> {
|
||||||
})
|
std::env::var(name)
|
||||||
|
.ok()
|
||||||
|
.and_then(|value| value.trim().parse::<u32>().ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_offset(instant: Instant, offset_us: i64) -> Instant {
|
fn apply_offset(instant: Instant, offset_us: i64) -> Instant {
|
||||||
|
|||||||
@ -378,9 +378,9 @@ fn server_rc_mode_matrix_validates_advertised_uvc_profiles() {
|
|||||||
"LESAVKA_SERVER_RC_MODES=${LESAVKA_SERVER_RC_MODES:-${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES}}",
|
"LESAVKA_SERVER_RC_MODES=${LESAVKA_SERVER_RC_MODES:-${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES}}",
|
||||||
"LESAVKA_SERVER_REPO=${LESAVKA_SERVER_REPO:-auto}",
|
"LESAVKA_SERVER_REPO=${LESAVKA_SERVER_REPO:-auto}",
|
||||||
"LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US:-${LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US:-0}}",
|
"LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US:-${LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US:-0}}",
|
||||||
"LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US:-170000}",
|
"LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US:-135090}",
|
||||||
"LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US:-1280x720@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1280x720@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US}}",
|
"LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US:-1280x720@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1280x720@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US}}",
|
||||||
"LESAVKA_SERVER_RC_MODE_DELAYS_US=${LESAVKA_SERVER_RC_MODE_DELAYS_US:-1280x720@20=170000,1280x720@30=170000,1920x1080@20=170000,1920x1080@30=170000}",
|
"LESAVKA_SERVER_RC_MODE_DELAYS_US=${LESAVKA_SERVER_RC_MODE_DELAYS_US:-1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952}",
|
||||||
"LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES=${LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES:-1280x720,1920x1080}",
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES=${LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES:-1280x720,1920x1080}",
|
||||||
"LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS=${LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS:-20,30}",
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS=${LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS:-20,30}",
|
||||||
"LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX:-Logitech|BRIO|C9[0-9]+|HD UVC WebCam|USB2[.]0 HD|Integrated Camera|Webcam|Camera}",
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX:-Logitech|BRIO|C9[0-9]+|HD UVC WebCam|USB2[.]0 HD|Integrated Camera|Webcam|Camera}",
|
||||||
|
|||||||
@ -21,6 +21,8 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
"LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS=%s",
|
"LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS=%s",
|
||||||
"LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS=%s",
|
"LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS=%s",
|
||||||
"LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS=%s",
|
"LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS=%s",
|
||||||
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US=%s",
|
||||||
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US=%s",
|
||||||
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=%s",
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=%s",
|
||||||
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=%s",
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=%s",
|
||||||
"LESAVKA_UPSTREAM_PAIR_SLACK_US=%s",
|
"LESAVKA_UPSTREAM_PAIR_SLACK_US=%s",
|
||||||
@ -56,7 +58,13 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS:-1000}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UPSTREAM_MAX_LIVE_LAG_MS:-1000}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS:-60000}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UPSTREAM_STARTUP_TIMEOUT_MS:-60000}"));
|
||||||
assert!(SERVER_INSTALL.contains("DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=0"));
|
assert!(SERVER_INSTALL.contains("DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US=0"));
|
||||||
assert!(SERVER_INSTALL.contains("DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=170000"));
|
assert!(SERVER_INSTALL.contains("DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=135090"));
|
||||||
|
assert!(SERVER_INSTALL.contains(
|
||||||
|
"DEFAULT_MJPEG_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US=1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0"
|
||||||
|
));
|
||||||
|
assert!(SERVER_INSTALL.contains(
|
||||||
|
"DEFAULT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US=1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952"
|
||||||
|
));
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("resolve_upstream_video_playout_offset_us"),
|
SERVER_INSTALL.contains("resolve_upstream_video_playout_offset_us"),
|
||||||
"video offset should be resolved through stale-baseline migration logic"
|
"video offset should be resolved through stale-baseline migration logic"
|
||||||
@ -72,6 +80,9 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=130000")
|
SERVER_INSTALL.contains("PREVIOUS_BROWSER_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=130000")
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("PREVIOUS_SCALAR_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=170000")
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=1090000")
|
SERVER_INSTALL.contains("PREVIOUS_OVERSHOT_MJPEG_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US=1090000")
|
||||||
);
|
);
|
||||||
@ -84,12 +95,14 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
"install-specific video offset override should bypass stale ambient runtime env"
|
"install-specific video offset override should bypass stale ambient runtime env"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("migrating stale upstream audio playout offset to the 0.17 freshness-first planner default"),
|
SERVER_INSTALL.contains(
|
||||||
|
"migrating stale upstream audio playout offset to the per-mode MJPEG/UAC default"
|
||||||
|
),
|
||||||
"installer should not preserve old MJPEG/UVC sync baselines accidentally"
|
"installer should not preserve old MJPEG/UVC sync baselines accidentally"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains(
|
SERVER_INSTALL.contains(
|
||||||
"migrating stale upstream video playout offset to the direct UVC/UAC MJPEG sync center"
|
"migrating stale upstream video playout offset to the per-mode direct UVC/UAC MJPEG sync center"
|
||||||
),
|
),
|
||||||
"installer should not preserve old video delay baselines accidentally"
|
"installer should not preserve old video delay baselines accidentally"
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user