diff --git a/.dockerignore b/.dockerignore index 305065fe..9d9d0206 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ **/target/ **/.git -**/.env \ No newline at end of file +**/.env +**/.idea diff --git a/containers/runner.Containerfile b/containers/runner.Containerfile index fec41b43..c4484526 100644 --- a/containers/runner.Containerfile +++ b/containers/runner.Containerfile @@ -86,7 +86,7 @@ RUN --mount=type=cache,target=/var/cache/pacman/pkg \ libxkbcommon wayland gstreamer gst-plugins-base gst-plugins-good libinput # Clone repository -RUN git clone -b dev-dmabuf https://github.com/DatCaptainHorse/gst-wayland-display.git +RUN git clone --depth 1 -b "dev-dmabuf" https://github.com/DatCaptainHorse/gst-wayland-display.git #-------------------------------------------------------------------- FROM gst-wayland-deps AS gst-wayland-planner @@ -135,11 +135,12 @@ RUN --mount=type=cache,target=/var/cache/pacman/pkg \ vulkan-intel lib32-vulkan-intel vpl-gpu-rt \ vulkan-radeon lib32-vulkan-radeon \ mesa \ - steam steam-native-runtime gtk3 lib32-gtk3 \ - sudo xorg-xwayland seatd libinput gamescope mangohud \ + steam steam-native-runtime proton-cachyos gtk3 lib32-gtk3 \ + sudo xorg-xwayland seatd libinput gamescope mangohud wlr-randr \ libssh2 curl wget \ pipewire pipewire-pulse pipewire-alsa wireplumber \ - noto-fonts-cjk supervisor jq chwd lshw pacman-contrib && \ + noto-fonts-cjk supervisor jq chwd lshw pacman-contrib \ + openssh && \ # GStreamer stack pacman -Sy --needed --noconfirm \ gstreamer gst-plugins-base gst-plugins-good \ @@ -153,14 +154,6 @@ RUN --mount=type=cache,target=/var/cache/pacman/pkg \ paccache -rk1 && \ rm -rf /usr/share/{info,man,doc}/* -### Application Installation ### -ARG LUDUSAVI_VERSION="0.28.0" -RUN curl -fsSL -o ludusavi.tar.gz \ - "https://github.com/mtkennerly/ludusavi/releases/download/v${LUDUSAVI_VERSION}/ludusavi-v${LUDUSAVI_VERSION}-linux.tar.gz" && \ - tar -xzvf ludusavi.tar.gz && \ - mv ludusavi /usr/bin/ && \ - rm ludusavi.tar.gz - ### User Configuration ### ENV USER="nestri" \ UID=1000 \ diff --git a/packages/scripts/_v2-entry-point b/packages/scripts/_v2-entry-point new file mode 100755 index 00000000..2e014b88 --- /dev/null +++ b/packages/scripts/_v2-entry-point @@ -0,0 +1,10 @@ +#!/bin/bash + +while true; do + case "$1" in + --*) shift ;; + *) break ;; + esac +done + +exec "$@" diff --git a/packages/scripts/entrypoint.sh b/packages/scripts/entrypoint.sh index 06747d25..5599a6e7 100644 --- a/packages/scripts/entrypoint.sh +++ b/packages/scripts/entrypoint.sh @@ -13,7 +13,7 @@ log() { # Ensures user directory ownership chown_user_directory() { local user_group="${USER}:${GID}" - if ! chown -R -h --no-preserve-root "$user_group" "${HOME}" 2>/dev/null; then + if ! chown -h --no-preserve-root "$user_group" "${HOME}" 2>/dev/null; then echo "Error: Failed to change ownership of ${HOME} to ${user_group}" >&2 return 1 fi @@ -36,6 +36,12 @@ wait_for_socket() { return 1 } +# Prepares environment for namespace-less applications (like Steam) +setup_namespaceless() { + rm -f /run/systemd/container || true + mkdir -p /run/pressure-vessel || true +} + # Ensures cache directory exists setup_cache() { log "Setting up NVIDIA driver cache directory at $CACHE_DIR..." @@ -103,8 +109,10 @@ get_nvidia_installer() { install_nvidia_driver() { local filename="$1" log "Installing NVIDIA driver components from $filename..." - sudo ./"$filename" \ + bash ./"$filename" \ --silent \ + --skip-depmod \ + --skip-module-unload \ --no-kernel-module \ --install-compat32-libs \ --no-nouveau-check \ @@ -116,18 +124,102 @@ install_nvidia_driver() { log "Error: NVIDIA driver installation failed." return 1 } + + # Install CUDA package + log "Checking if CUDA is already installed" + if ! pacman -Q cuda &>/dev/null; then + log "Installing CUDA package" + pacman -S --noconfirm cuda --assume-installed opencl-nvidia + else + log "CUDA package is already installed, skipping" + fi + log "NVIDIA driver installation completed." return 0 } -function log_gpu_info { +log_gpu_info() { + if ! declare -p vendor_devices &>/dev/null; then + log "Warning: vendor_devices array is not defined" + return + fi + log "Detected GPUs:" for vendor in "${!vendor_devices[@]}"; do log "> $vendor: ${vendor_devices[$vendor]}" done } +configure_ssh() { + # Return early if SSH not enabled + if [ -z "${SSH_ENABLE_PORT+x}" ] || [ "${SSH_ENABLE_PORT:-0}" -eq 0 ]; then + return 0 + fi + + # Check if we have required key + if [ -z "${SSH_ALLOWED_KEY+x}" ] || [ -z "${SSH_ALLOWED_KEY}" ]; then + return 0 + fi + + log "Configuring SSH server on port ${SSH_ENABLE_PORT} with public key authentication" + + # Ensure SSH host keys exist + ssh-keygen -A 2>/dev/null || { + log "Error: Failed to generate SSH host keys" + return 1 + } + + # Create .ssh directory and authorized_keys file for nestri user + mkdir -p /home/nestri/.ssh + echo "${SSH_ALLOWED_KEY}" > /home/nestri/.ssh/authorized_keys + chmod 700 /home/nestri/.ssh + chmod 600 /home/nestri/.ssh/authorized_keys + chown -R nestri:nestri /home/nestri/.ssh + + # Update SSHD config + sed -i -E "s/^#?Port .*/Port ${SSH_ENABLE_PORT}/" /etc/ssh/sshd_config || { + log "Error: Failed to update SSH port configuration" + return 1 + } + + # Configure secure SSH settings + { + echo "PasswordAuthentication no" + echo "PermitRootLogin no" + echo "ChallengeResponseAuthentication no" + echo "UsePAM no" + echo "PubkeyAuthentication yes" + } | while read -r line; do + grep -qF "$line" /etc/ssh/sshd_config || echo "$line" >> /etc/ssh/sshd_config + done + + # Start SSH server + log "Starting SSH server on port ${SSH_ENABLE_PORT}" + /usr/sbin/sshd -D -p "${SSH_ENABLE_PORT}" & + SSH_PID=$! + + # Verify the process started + if ! ps -p $SSH_PID > /dev/null 2>&1; then + log "Error: SSH server failed to start" + return 1 + fi + + log "SSH server started with PID ${SSH_PID}" + return 0 +} + main() { + # Configure SSH + if [ -n "${SSH_ENABLE_PORT+x}" ] && [ "${SSH_ENABLE_PORT:-0}" -ne 0 ] && \ + [ -n "${SSH_ALLOWED_KEY+x}" ] && [ -n "${SSH_ALLOWED_KEY}" ]; then + if ! configure_ssh; then + log "Error: SSH configuration failed with given variables - exiting" + exit 1 + fi + else + log "SSH not configured (missing SSH_ENABLE_PORT or SSH_ALLOWED_KEY)" + fi + # Wait for required sockets wait_for_socket "/run/dbus/system_bus_socket" "DBus" || exit 1 wait_for_socket "/run/user/${UID}/pipewire-0" "PipeWire" || exit 1 @@ -215,6 +307,10 @@ main() { log "Ensuring user directory permissions..." chown_user_directory || exit 1 + # Setup namespaceless env + log "Applying namespace-less configuration" + setup_namespaceless + # Switch to nestri user log "Switching to nestri user for application startup..." if [[ ! -x /etc/nestri/entrypoint_nestri.sh ]]; then diff --git a/packages/scripts/entrypoint_nestri.sh b/packages/scripts/entrypoint_nestri.sh index 1ff1d6bd..5955dd27 100644 --- a/packages/scripts/entrypoint_nestri.sh +++ b/packages/scripts/entrypoint_nestri.sh @@ -1,5 +1,4 @@ #!/bin/bash -set -euo pipefail log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" @@ -50,6 +49,70 @@ kill_if_running() { fi } +# Starts up Steam namespace-less live-patcher +start_steam_namespaceless_patcher() { + kill_if_running "${PATCHER_PID:-}" "steam-patcher" + + local entrypoints=( + "${HOME}/.local/share/Steam/steamrt64/steam-runtime-steamrt/_v2-entry-point" + "${HOME}/.local/share/Steam/steamapps/common/SteamLinuxRuntime_soldier/_v2-entry-point" + "${HOME}/.local/share/Steam/steamapps/common/SteamLinuxRuntime_sniper/_v2-entry-point" + # < Add more entrypoints here if needed > + ) + local custom_entrypoint="/etc/nestri/_v2-entry-point" + local temp_entrypoint="/tmp/_v2-entry-point.padded" + + if [[ ! -f "$custom_entrypoint" ]]; then + log "Error: Custom _v2-entry-point not found at $custom_entrypoint" + exit 1 + fi + + log "Starting Steam _v2-entry-point patcher..." + ( + while true; do + for i in "${!entrypoints[@]}"; do + local steam_entrypoint="${entrypoints[$i]}" + + if [[ -f "$steam_entrypoint" ]]; then + # Get original file size + local original_size + original_size=$(stat -c %s "$steam_entrypoint" 2>/dev/null) + if [[ -z "$original_size" ]] || [[ "$original_size" -eq 0 ]]; then + log "Warning: Could not determine size of $steam_entrypoint, retrying..." + continue + fi + + # Copy custom entrypoint to temp location + cp "$custom_entrypoint" "$temp_entrypoint" 2>/dev/null || { + log "Warning: Failed to copy custom entrypoint to $temp_entrypoint" + continue + } + + # Pad the temporary file to match original size + if (( $(stat -c %s "$temp_entrypoint") < original_size )); then + truncate -s "$original_size" "$temp_entrypoint" 2>/dev/null || { + log "Warning: Failed to pad $temp_entrypoint to $original_size bytes" + continue + } + fi + + # Copy padded file to Steam's entrypoint, if contents differ + if ! cmp -s "$temp_entrypoint" "$steam_entrypoint"; then + cp "$temp_entrypoint" "$steam_entrypoint" 2>/dev/null || { + log "Warning: Failed to patch $steam_entrypoint" + } + fi + fi + done + + # Sleep for 1s + sleep 1 + done + ) & + PATCHER_PID=$! + log "Steam _v2-entry-point patcher started (PID: $PATCHER_PID)" +} + # Starts nestri-server start_nestri_server() { kill_if_running "${NESTRI_PID:-}" "nestri-server" @@ -71,33 +134,93 @@ start_nestri_server() { done log "Error: Wayland display 'wayland-1' not available." + + # Workaround for gstreamer being bit slow at times + log "Clearing gstreamer cache.." + rm -rf "${HOME}/.cache/gstreamer-1.0" 2>/dev/null || true + increment_retry "nestri-server" restart_chain } -# Starts compositor (gamescope) with Steam +# Starts compositor with optional application start_compositor() { kill_if_running "${COMPOSITOR_PID:-}" "compositor" + kill_if_running "${APP_PID:-}" "application" - log "Starting compositor with Steam..." - rm -rf /tmp/.X11-unix && mkdir -p /tmp/.X11-unix && chown nestri:nestri /tmp/.X11-unix - WAYLAND_DISPLAY=wayland-1 gamescope --backend wayland -g -f -e --rt --mangoapp -W "${WIDTH}" -H "${HEIGHT}" -- steam-native -tenfoot -cef-force-gpu & - COMPOSITOR_PID=$! + # Set default values only if variables are unset (not empty) + if [[ -z "${NESTRI_LAUNCH_CMD+x}" ]]; then + NESTRI_LAUNCH_CMD="steam-native -tenfoot -cef-force-gpu" + fi + if [[ -z "${NESTRI_LAUNCH_COMPOSITOR+x}" ]]; then + NESTRI_LAUNCH_COMPOSITOR="gamescope --backend wayland --force-grab-cursor -g -f --rt --mangoapp -W ${WIDTH} -H ${HEIGHT} -r ${FRAMERATE:-60}" + fi - log "Waiting for compositor to initialize..." - COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/gamescope-0" - for ((i=1; i<=15; i++)); do - if [[ -e "$COMPOSITOR_SOCKET" ]]; then - log "Compositor initialized, gamescope-0 ready." - sleep 2 - return + # Start Steam patcher only if Steam command is present + if [[ -n "${NESTRI_LAUNCH_CMD}" ]] && [[ "$NESTRI_LAUNCH_CMD" == *"steam"* ]]; then + start_steam_namespaceless_patcher + fi + + # Launch compositor if configured + if [[ -n "${NESTRI_LAUNCH_COMPOSITOR}" ]]; then + local compositor_cmd="$NESTRI_LAUNCH_COMPOSITOR" + local is_gamescope=false + + # Check if this is a gamescope command + if [[ "$compositor_cmd" == *"gamescope"* ]]; then + is_gamescope=true + # Append application command for gamescope if needed + if [[ -n "$NESTRI_LAUNCH_CMD" ]] && [[ "$compositor_cmd" != *" -- "* ]]; then + # If steam in launch command, enable gamescope integration via -e + if [[ "$NESTRI_LAUNCH_CMD" == *"steam"* ]]; then + compositor_cmd+=" -e" + fi + compositor_cmd+=" -- $NESTRI_LAUNCH_CMD" + fi fi - sleep 1 - done - log "Error: Compositor did not initialize." - increment_retry "compositor" - start_compositor + log "Starting compositor: $compositor_cmd" + WAYLAND_DISPLAY=wayland-1 /bin/bash -c "$compositor_cmd" & + COMPOSITOR_PID=$! + + # Wait for appropriate socket based on compositor type + if $is_gamescope; then + COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/gamescope-0" + log "Waiting for gamescope socket..." + else + COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/wayland-0" + log "Waiting for wayland-0 socket..." + fi + + for ((i=1; i<=15; i++)); do + if [[ -e "$COMPOSITOR_SOCKET" ]]; then + log "Compositor socket ready ($COMPOSITOR_SOCKET)." + # Patch resolution with wlr-randr for non-gamescope compositors + if ! $is_gamescope; then + local OUTPUT_NAME + OUTPUT_NAME=$(WAYLAND_DISPLAY=wayland-0 wlr-randr --json | jq -r '.[] | select(.enabled == true) | .name' | head -n 1) + if [ -z "$OUTPUT_NAME" ]; then + log "Warning: No enabled outputs detected. Skipping wlr-randr resolution patch." + return + fi + WAYLAND_DISPLAY=wayland-0 wlr-randr --output "$OUTPUT_NAME" --custom-mode "$WIDTH"x"$HEIGHT" + log "Patched resolution with wlr-randr." + fi + return + fi + sleep 1 + done + log "Warning: Compositor socket not found after 15 seconds ($COMPOSITOR_SOCKET)." + else + # Launch standalone application if no compositor + if [[ -n "${NESTRI_LAUNCH_CMD}" ]]; then + log "Starting application: $NESTRI_LAUNCH_CMD" + WAYLAND_DISPLAY=wayland-1 /bin/bash -c "$NESTRI_LAUNCH_CMD" & + APP_PID=$! + else + log "No compositor or application configured." + fi + fi } # Increments retry counter @@ -113,21 +236,24 @@ increment_retry() { # Restarts the chain restart_chain() { log "Restarting nestri-server and compositor..." - RETRY_COUNT=0 start_nestri_server } # Cleans up processes cleanup() { + local exit_code=$? log "Terminating processes..." kill_if_running "${NESTRI_PID:-}" "nestri-server" kill_if_running "${COMPOSITOR_PID:-}" "compositor" - exit 0 + kill_if_running "${APP_PID:-}" "application" + kill_if_running "${PATCHER_PID:-}" "steam-patcher" + rm -f "/tmp/_v2-entry-point.padded" 2>/dev/null + exit $exit_code } # Monitor processes for unexpected exits main_loop() { - trap cleanup SIGINT SIGTERM + trap cleanup SIGINT SIGTERM EXIT while true; do sleep 1 @@ -141,6 +267,16 @@ main_loop() { log "compositor died." increment_retry "compositor" start_compositor + # Check application + elif [[ -n "${APP_PID:-}" ]] && ! kill -0 "${APP_PID}" 2>/dev/null; then + log "application died." + increment_retry "application" + start_compositor + # Check patcher + elif [[ -n "${PATCHER_PID:-}" ]] && ! kill -0 "${PATCHER_PID}" 2>/dev/null; then + log "steam-patcher died." + increment_retry "steam-patcher" + start_steam_namespaceless_patcher fi done } diff --git a/packages/scripts/envs.sh b/packages/scripts/envs.sh index f97f1a72..72741fb4 100644 --- a/packages/scripts/envs.sh +++ b/packages/scripts/envs.sh @@ -1,5 +1,4 @@ #!/bin/bash -set -euo pipefail export XDG_RUNTIME_DIR=/run/user/${UID}/ export XDG_SESSION_TYPE=x11 @@ -11,3 +10,7 @@ export PROTON_NO_FSYNC=1 # Sleeker Mangohud preset :) export MANGOHUD_CONFIG=preset=2 + +# Make gstreamer GL elements work without display output (NVIDIA issue..) +export GST_GL_API=gles2 +export GST_GL_WINDOW=surfaceless diff --git a/packages/scripts/supervisord.conf b/packages/scripts/supervisord.conf index ea1604a3..91f458fc 100644 --- a/packages/scripts/supervisord.conf +++ b/packages/scripts/supervisord.conf @@ -54,3 +54,6 @@ autorestart=false autostart=true startretries=0 priority=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +redirect_stderr=true diff --git a/packages/server/Cargo.lock b/packages/server/Cargo.lock index 6a78499c..964d8a40 100644 --- a/packages/server/Cargo.lock +++ b/packages/server/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -111,33 +111,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", @@ -326,6 +326,48 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-config" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455e9fb7743c6f6267eb2830ccc08686fbb3d13c9a689369562fd4d4ef9ea462" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 1.3.1", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + [[package]] name = "aws-lc-rs" version = "1.13.1" @@ -349,6 +391,361 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "aws-runtime" +version = "1.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6c68419d8ba16d9a7463671593c54f81ba58cab466e9b759418da606dcc2e2" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-kinesisvideo" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9684b455fb5bd87ba61fca7444add088724f41ba115e6326dcb1b45d1d6ee348" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-kinesisvideosignaling" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af014a33946080634dc1b99a81d46d4e2bc59e1ccc1b95049bde219a03a83e01" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac1674cba7872061a29baaf02209fefe499ff034dfd91bd4cc59e4d7741489" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6a22f077f5fd3e3c0270d4e1a110346cddf6769e9433eb9e6daceb4ca3b149" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d440e1d368759bd10df0dbdddbfff6473d7cd73e9d9ef2363dc9995ac2d711" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfb9021f581b71870a17eac25b52335b82211cdc092e02b6876b2bcefa61666" +dependencies = [ + "aws-credential-types", + "aws-smithy-http 0.62.1", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.62.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f491388e741b7ca73b24130ff464c1478acc34d5b331b7dd0a2ee4643595a15" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.26", + "h2 0.4.10", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.7", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16e040799d29c17412943bdbf488fd75db04112d0c0d4b9290bacf5ae0014b9" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http 0.62.1", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8531b6d8882fd8f48f82a9754e682e29dd44cff27154af51fa3eb730f59efb" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.3.1", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d498595448e43de7f4296b7b7a18a8a02c61ec9349128c80a368f7c3b4ab11a8" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db87b96cb1b16c024980f133968d52882ca0daaee3a086c6decc500f6c99728" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -389,10 +786,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "base64ct" -version = "1.7.3" +name = "base64-simd" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bincode" @@ -476,9 +883,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byteorder" @@ -495,6 +902,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "cbc" version = "0.1.2" @@ -506,9 +923,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.24" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -548,9 +965,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -620,9 +1037,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -630,9 +1047,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -642,11 +1059,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -654,9 +1071,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cmake" @@ -669,9 +1086,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "concurrent-queue" @@ -698,6 +1115,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -836,6 +1263,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -1012,7 +1453,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -1077,6 +1518,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fnv" version = "1.0.7" @@ -1257,7 +1704,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows 0.61.1", + "windows 0.61.3", ] [[package]] @@ -1280,7 +1727,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1316,8 +1763,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a5c3829f5794cb15120db87707b2ec03720edff7ad09eb7b711b532e3fe747" dependencies = [ "futures-channel", "futures-core", @@ -1332,8 +1780,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" dependencies = [ "glib-sys", "gobject-sys", @@ -1344,8 +1793,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2" dependencies = [ "bitflags 2.9.1", "futures-channel", @@ -1364,10 +1814,11 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro-crate", "proc-macro2", "quote", @@ -1376,8 +1827,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" dependencies = [ "libc", "system-deps", @@ -1391,8 +1843,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gobject-sys" -version = "0.21.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=main#1b7f04c17807ab4b09a761eb3d90efdf2605ab47" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" dependencies = [ "glib-sys", "libc", @@ -1412,8 +1865,9 @@ dependencies = [ [[package]] name = "gst-plugin-version-helper" -version = "0.8.1" -source = "git+https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs?branch=main#1e243add0ccd0bfff276cd6a593173561261b5b4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5e874f1660252fd2ec81c602066df3633b3a6fcbe2b196f7f93c27cf069b2a" dependencies = [ "chrono", "toml_edit", @@ -1421,18 +1875,27 @@ dependencies = [ [[package]] name = "gst-plugin-webrtc" -version = "0.14.0-alpha.1" -source = "git+https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs?branch=main#1e243add0ccd0bfff276cd6a593173561261b5b4" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a5c83ae8a8ed03c689884b55be023725a0a3fa6e64834987a0c876f6829ee7" dependencies = [ "anyhow", "async-recursion", "async-tungstenite", + "aws-config", + "aws-credential-types", + "aws-sdk-kinesisvideo", + "aws-sdk-kinesisvideosignaling", + "aws-sigv4", + "aws-smithy-http 0.60.12", + "aws-smithy-types", + "aws-types", "chrono", "ctrlc", + "data-encoding", "fastrand", "futures", "gst-plugin-version-helper", - "gst-plugin-webrtc-signalling", "gst-plugin-webrtc-signalling-protocol", "gstreamer", "gstreamer-app", @@ -1446,51 +1909,29 @@ dependencies = [ "gstreamer-webrtc", "http 1.3.1", "human_bytes", - "itertools 0.14.0", + "livekit-api", + "livekit-protocol", + "once_cell", "parse_link_header", "rand 0.9.1", - "reqwest", + "reqwest 0.12.20", "serde", "serde_json", "thiserror 2.0.12", "tokio", "tokio-native-tls", "tokio-stream", - "tracing", - "tracing-log", - "tracing-subscriber", "url", + "url-escape", "uuid", "warp", ] -[[package]] -name = "gst-plugin-webrtc-signalling" -version = "0.14.0-alpha.1" -source = "git+https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs?branch=main#1e243add0ccd0bfff276cd6a593173561261b5b4" -dependencies = [ - "anyhow", - "async-tungstenite", - "clap", - "futures", - "gst-plugin-webrtc-signalling-protocol", - "pin-project-lite", - "serde", - "serde_json", - "test-log", - "thiserror 2.0.12", - "tokio", - "tokio-native-tls", - "tracing", - "tracing-log", - "tracing-subscriber", - "uuid", -] - [[package]] name = "gst-plugin-webrtc-signalling-protocol" -version = "0.14.0-alpha.1" -source = "git+https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs?branch=main#1e243add0ccd0bfff276cd6a593173561261b5b4" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd6bb4c991682bf22ce3d79dc285b4e51ff1fdce75362778a281f8d97644cef" dependencies = [ "serde", "serde_json", @@ -1498,8 +1939,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ab4c88f731596a2511a6f14cabdd666e0d8efab62a1d58e6ddb57faa96e22e" dependencies = [ "cfg-if", "futures-channel", @@ -1508,11 +1950,11 @@ dependencies = [ "glib", "gstreamer-sys", "itertools 0.14.0", - "kstring", "libc", "muldiv", "num-integer", "num-rational", + "once_cell", "option-operations", "paste", "pin-project-lite", @@ -1524,8 +1966,9 @@ dependencies = [ [[package]] name = "gstreamer-app" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9a883eb21aebcf1289158225c05f7aea5da6ecf71fa7f0ff1ce4d25baf004e" dependencies = [ "futures-core", "futures-sink", @@ -1538,8 +1981,9 @@ dependencies = [ [[package]] name = "gstreamer-app-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f7ef838306fe51852d503a14dc79ac42de005a59008a05098de3ecdaf05455" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -1550,8 +1994,9 @@ dependencies = [ [[package]] name = "gstreamer-audio" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7ec7e0374298897e669db7c79544bc44df12011985e7dd5f38644edaf2caf4" dependencies = [ "cfg-if", "glib", @@ -1559,14 +2004,16 @@ dependencies = [ "gstreamer-audio-sys", "gstreamer-base", "libc", + "once_cell", "serde", "smallvec", ] [[package]] name = "gstreamer-audio-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5f3e09e7c04ec91d78c2a6ca78d50b574b9ed49fdf5e72f3693adca4306a87" dependencies = [ "glib-sys", "gobject-sys", @@ -1578,8 +2025,9 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19a74fd04ffdcb847dd322640f2cf520897129d00a7bcb92fd62a63f3e27404" dependencies = [ "atomic_refcell", "cfg-if", @@ -1591,8 +2039,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f2fb0037b6d3c5b51f60dea11e667910f33be222308ca5a101450018a09840" dependencies = [ "glib-sys", "gobject-sys", @@ -1603,8 +2052,9 @@ dependencies = [ [[package]] name = "gstreamer-net" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a57013f1af740373c5cbf1602b6ff25238d4a0e300eff179b75676c9c7781e" dependencies = [ "gio", "glib", @@ -1614,8 +2064,9 @@ dependencies = [ [[package]] name = "gstreamer-net-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0291a3064e6e94d640f2d662264b726e5e0c2067ceb17f2b56dec6f6920877" dependencies = [ "gio-sys", "glib-sys", @@ -1626,8 +2077,9 @@ dependencies = [ [[package]] name = "gstreamer-rtp" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf35002f6eff177f5afc255fa7a2e7ab718fe8186a737dfdb791784e63571154" dependencies = [ "glib", "gstreamer", @@ -1637,8 +2089,9 @@ dependencies = [ [[package]] name = "gstreamer-rtp-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b874248d40bd6678be9c7218ec880735258cf2ba77000c72efda8bb3271b0fe" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -1649,8 +2102,9 @@ dependencies = [ [[package]] name = "gstreamer-sdp" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f94ab92cb1dbd6d00e41208ab463b5fbce3eca65a4c9710585fede015a9d65" dependencies = [ "glib", "gstreamer", @@ -1659,8 +2113,9 @@ dependencies = [ [[package]] name = "gstreamer-sdp-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de44d5e90138ac1786a6418a38c73d9a78ee0d15680129f09f91df5309d658e0" dependencies = [ "glib-sys", "gstreamer-sys", @@ -1670,10 +2125,10 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feea73b4d92dbf9c24a203c9cd0bcc740d584f6b5960d5faf359febf288919b2" dependencies = [ - "cfg-if", "glib-sys", "gobject-sys", "libc", @@ -1682,19 +2137,22 @@ dependencies = [ [[package]] name = "gstreamer-utils" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ff07fd1ba426dfe311baa90c7b27d1896725952f6edb91b70cd59c773e9d1a" dependencies = [ "gstreamer", "gstreamer-app", "gstreamer-video", + "once_cell", "thiserror 2.0.12", ] [[package]] name = "gstreamer-video" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1318b599d77ca4f7702ecbdeac1672d6304cb16b7e5752fabb3ee8260449a666" dependencies = [ "cfg-if", "futures-channel", @@ -1703,14 +2161,16 @@ dependencies = [ "gstreamer-base", "gstreamer-video-sys", "libc", + "once_cell", "serde", "thiserror 2.0.12", ] [[package]] name = "gstreamer-video-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a70f0947f12d253b9de9bc3fd92f981e4d025336c18389c7f08cdf388a99f5c" dependencies = [ "glib-sys", "gobject-sys", @@ -1722,8 +2182,9 @@ dependencies = [ [[package]] name = "gstreamer-webrtc" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c475e2fa45c6c14b971e2ac40e7bae035f19592cac68c391d12eb659fd1722b" dependencies = [ "glib", "gstreamer", @@ -1734,8 +2195,9 @@ dependencies = [ [[package]] name = "gstreamer-webrtc-sys" -version = "0.24.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#29ab13fdf98f9b58a85fd00fec44948173accc15" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0ce6dd5e17757933233bf3fce2226eb2e8c06ec2325c2459a1022ae1d7d279" dependencies = [ "glib-sys", "gstreamer-sdp-sys", @@ -1793,9 +2255,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -1835,6 +2297,12 @@ dependencies = [ "http 0.2.12", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1843,15 +2311,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2059,20 +2521,50 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", "rustls 0.23.27", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -2091,9 +2583,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ "base64 0.22.1", "bytes", @@ -2108,7 +2600,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", @@ -2263,7 +2755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" dependencies = [ "async-io", - "core-foundation", + "core-foundation 0.9.4", "fnv", "futures", "if-addrs", @@ -2274,7 +2766,7 @@ dependencies = [ "netlink-proto", "netlink-sys", "rtnetlink", - "system-configuration", + "system-configuration 0.6.1", "tokio", "windows 0.53.0", ] @@ -2307,7 +2799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2374,6 +2866,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -2419,12 +2920,16 @@ dependencies = [ ] [[package]] -name = "kstring" -version = "2.0.2" +name = "jsonwebtoken" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "static_assertions", + "base64 0.22.1", + "js-sys", + "ring", + "serde", + "serde_json", ] [[package]] @@ -2441,9 +2946,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] name = "libloading" @@ -2452,7 +2957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.53.2", ] [[package]] @@ -2467,6 +2972,7 @@ dependencies = [ "futures-timer", "getrandom 0.2.16", "libp2p-allow-block-list", + "libp2p-autonat", "libp2p-connection-limits", "libp2p-core", "libp2p-dns", @@ -2482,6 +2988,7 @@ dependencies = [ "libp2p-swarm", "libp2p-tcp", "libp2p-upnp", + "libp2p-websocket", "libp2p-yamux", "multiaddr", "pin-project", @@ -2500,6 +3007,31 @@ dependencies = [ "libp2p-swarm", ] +[[package]] +name = "libp2p-autonat" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e297bfc6cabb70c6180707f8fa05661b77ecb9cb67e8e8e1c469301358fa21d0" +dependencies = [ + "async-trait", + "asynchronous-codec", + "either", + "futures", + "futures-bounded", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-request-response", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "rand_core 0.6.4", + "thiserror 2.0.12", + "tracing", + "web-time", +] + [[package]] name = "libp2p-connection-limits" version = "0.5.0" @@ -2751,6 +3283,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "libp2p-request-response" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548fe44a80ff275d400f1b26b090d441d83ef73efabbeb6415f4ce37e5aed865" +dependencies = [ + "async-trait", + "futures", + "futures-bounded", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "rand 0.8.5", + "smallvec", + "tracing", +] + [[package]] name = "libp2p-stream" version = "0.3.0-alpha" @@ -2794,7 +3343,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -2850,6 +3399,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "libp2p-websocket" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf5d48a4d8fad8a49fbf23816a878cac25623549f415d74da8ef4327e6196a9" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "parking_lot", + "pin-project-lite", + "rw-stream-sink", + "soketto", + "thiserror 2.0.12", + "tracing", + "url", + "webpki-roots", +] + [[package]] name = "libp2p-yamux" version = "0.47.0" @@ -2884,10 +3454,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] -name = "lock_api" -version = "0.4.12" +name = "livekit-api" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "e20c3fc3de5944ce6b5c8da4084cf828bbae7216671e32e83b767ce61feeb7e0" +dependencies = [ + "futures-util", + "jsonwebtoken", + "livekit-protocol", + "log", + "parking_lot", + "prost 0.12.6", + "reqwest 0.11.27", + "scopeguard", + "serde", + "sha2", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite 0.20.1", + "url", +] + +[[package]] +name = "livekit-protocol" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a1bd23257110be29d024d8d816adff70df18ea1d22ceb1aab6f3ad4aab0d523" +dependencies = [ + "futures-util", + "parking_lot", + "pbjson", + "pbjson-types", + "prost 0.12.6", + "prost-types 0.12.6", + "serde", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2918,7 +3527,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2948,9 +3557,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -2985,9 +3594,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] @@ -2999,7 +3608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -3087,6 +3696,12 @@ dependencies = [ "unsigned-varint 0.8.0", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "multistream-select" version = "0.13.0" @@ -3113,7 +3728,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3126,21 +3741,22 @@ dependencies = [ "byteorder", "chrono", "clap", - "futures-util", + "dashmap", "gst-plugin-webrtc", "gstreamer", "gstreamer-webrtc", "libp2p", "libp2p-stream", "parking_lot", - "prost", - "prost-types", + "prost 0.14.0", + "prost-types 0.14.0", "rand 0.9.1", "regex", "rustls 0.23.27", "serde", "serde_json", "tokio", + "tokio-stream", "tracing", "tracing-subscriber", "webrtc", @@ -3308,11 +3924,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -3354,9 +3970,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags 2.9.1", "cfg-if", @@ -3386,9 +4002,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -3405,6 +4021,12 @@ dependencies = [ "paste", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "overload" version = "0.1.1" @@ -3443,9 +4065,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -3453,9 +4075,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -3482,6 +4104,43 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbjson" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "prost 0.12.6", + "prost-types 0.12.6", +] + +[[package]] +name = "pbjson-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost 0.12.6", + "prost-build", + "serde", +] + [[package]] name = "pem" version = "3.0.5" @@ -3507,6 +4166,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -3563,7 +4232,7 @@ checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.1", + "hermit-abi", "pin-project-lite", "rustix 1.0.7", "tracing", @@ -3595,9 +4264,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" @@ -3625,9 +4294,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" dependencies = [ "proc-macro2", "syn", @@ -3685,19 +4354,63 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.6", +] + +[[package]] +name = "prost" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe28332295ca4451b7d779aff2749b144cabe5e6e05fe86f31337831d7df232" +dependencies = [ + "bytes", + "prost-derive 0.14.0", +] + +[[package]] +name = "prost-build" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.12.6", + "prost-types 0.12.6", + "regex", + "syn", + "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ac3e3c6b0e1c219e61ceda600eaad26d7195ecc9b5c027925c904091374ab5" dependencies = [ "anyhow", "itertools 0.14.0", @@ -3708,11 +4421,20 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost", + "prost 0.12.6", +] + +[[package]] +name = "prost-types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bfff0dbd11dbadf180fea466aa146cdf20aed230e1c42b8bae192df8f0469a" +dependencies = [ + "prost 0.14.0", ] [[package]] @@ -3883,9 +4605,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -3922,6 +4644,12 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -3936,9 +4664,49 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.16" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf597b113be201cb2269b4c39b39a804d01b99ee95a4278f0ed04e45cff1c71" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "reqwest" +version = "0.12.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", @@ -3949,22 +4717,20 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.6.0", - "hyper-rustls", - "hyper-tls", + "hyper-rustls 0.27.7", + "hyper-tls 0.6.0", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", "tower", @@ -4052,9 +4818,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -4114,16 +4880,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "subtle", - "zeroize", + "rustls-webpki 0.101.7", + "sct", ] [[package]] @@ -4143,12 +4907,36 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "2.2.0" +name = "rustls-native-certs" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", ] [[package]] @@ -4171,17 +4959,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.103.3" @@ -4238,6 +5015,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sdp" version = "0.8.0" @@ -4271,7 +5058,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -4336,9 +5136,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -4422,9 +5222,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smol_str" @@ -4462,6 +5262,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + [[package]] name = "spin" version = "0.9.8" @@ -4532,15 +5347,21 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4561,6 +5382,17 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys 0.5.0", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -4568,8 +5400,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", - "core-foundation", - "system-configuration-sys", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -4589,7 +5431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", "toml", "version-compare", @@ -4620,27 +5462,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "test-log" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f46083d221181166e5b6f6b1e5f1d499f3a76888826e6cb1d057554157cd0f" -dependencies = [ - "test-log-macros", - "tracing-subscriber", -] - -[[package]] -name = "test-log-macros" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "888d0c3c6db53c0fdab160d2ed5e12ba745383d3e85813f2ea0f2b1475ab553f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -4683,12 +5504,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -4788,12 +5608,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", + "rustls 0.21.12", "tokio", ] @@ -4816,6 +5635,21 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "native-tls", + "tokio", + "tokio-native-tls", + "tungstenite 0.20.1", ] [[package]] @@ -4845,9 +5679,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -4857,18 +5691,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", @@ -4886,7 +5720,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -4894,9 +5728,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags 2.9.1", "bytes", @@ -4936,9 +5770,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", @@ -4947,9 +5781,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -4990,6 +5824,26 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 0.2.12", + "httparse", + "log", + "native-tls", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.21.0" @@ -5118,6 +5972,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "url-escape" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -5171,6 +6040,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "waitgroup" version = "0.1.2" @@ -5207,14 +6082,12 @@ dependencies = [ "multer", "percent-encoding", "pin-project", - "rustls-pemfile", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.25.0", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "tokio-util", "tower-service", "tracing", @@ -5222,9 +6095,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -5326,6 +6199,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webrtc" version = "0.13.0" @@ -5587,9 +6466,9 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core 0.61.2", @@ -5627,7 +6506,7 @@ dependencies = [ "windows-interface", "windows-link", "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-strings", ] [[package]] @@ -5665,9 +6544,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -5681,13 +6560,13 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" dependencies = [ + "windows-link", "windows-result 0.3.4", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-strings", ] [[package]] @@ -5708,15 +6587,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -5786,9 +6656,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -5949,9 +6819,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -6017,6 +6887,12 @@ version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "xmltree" version = "0.10.3" diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index e8658530..c3202e79 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -8,25 +8,26 @@ name = "nestri-server" path = "src/main.rs" [dependencies] -gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_26"] } -gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_26"] } -gstrswebrtc = { package = "gst-plugin-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", branch = "main" } +gstreamer = { version = "0.23", features = ["v1_26"] } +gstreamer-webrtc = { version = "0.23", features = ["v1_26"] } +gst-plugin-webrtc = { version = "0.13", features = ["v1_22"] } serde = {version = "1.0", features = ["derive"] } -tokio = { version = "1.44", features = ["full"] } -clap = { version = "4.5", features = ["env"] } +tokio = { version = "1.45", features = ["full"] } +tokio-stream = { version = "0.1", features = ["full"] } +clap = { version = "4.5", features = ["env", "derive"] } serde_json = "1.0" webrtc = "0.13" regex = "1.11" rand = "0.9" rustls = { version = "0.23", features = ["ring"] } tracing = "0.1" -tracing-subscriber = "0.3" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } chrono = "0.4" -futures-util = "0.3" -prost = "0.13" -prost-types = "0.13" +prost = "0.14" +prost-types = "0.14" parking_lot = "0.12" atomic_refcell = "0.1" byteorder = "1.5" -libp2p = { version = "0.55", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros"] } -libp2p-stream = "0.3.0-alpha" \ No newline at end of file +libp2p = { version = "0.55", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros", "websocket", "autonat"] } +libp2p-stream = { version = "0.3.0-alpha" } +dashmap = "6.1" diff --git a/packages/server/src/args.rs b/packages/server/src/args.rs index ad3df445..d49f2337 100644 --- a/packages/server/src/args.rs +++ b/packages/server/src/args.rs @@ -1,7 +1,7 @@ use crate::args::encoding_args::AudioCaptureMethod; use crate::enc_helper::{AudioCodec, EncoderType, VideoCodec}; -use clap::{Arg, Command, value_parser}; use clap::builder::{BoolishValueParser, NonEmptyStringValueParser}; +use clap::{Arg, Command, value_parser}; pub mod app_args; pub mod device_args; @@ -89,7 +89,7 @@ impl Args { .env("GPU_INDEX") .help("GPU to use by index") .value_parser(value_parser!(i32).range(-1..)) - .default_value("-1") + .default_value("-1"), ) .arg( Arg::new("gpu-card-path") @@ -160,7 +160,7 @@ impl Args { .env("AUDIO_CAPTURE_METHOD") .help("Audio capture method") .value_parser(value_parser!(AudioCaptureMethod)) - .default_value("pipewire"), + .default_value("pulseaudio"), ) .arg( Arg::new("audio-codec") diff --git a/packages/server/src/args/app_args.rs b/packages/server/src/args/app_args.rs index 37cef070..754579af 100644 --- a/packages/server/src/args/app_args.rs +++ b/packages/server/src/args/app_args.rs @@ -55,7 +55,11 @@ impl AppArgs { tracing::info!("AppArgs:"); tracing::info!("> verbose: {}", self.verbose); tracing::info!("> debug: {}", self.debug); - tracing::info!("> resolution: '{}x{}'", self.resolution.0, self.resolution.1); + tracing::info!( + "> resolution: '{}x{}'", + self.resolution.0, + self.resolution.1 + ); tracing::info!("> framerate: {}", self.framerate); tracing::info!("> relay_url: '{}'", self.relay_url); tracing::info!("> room: '{}'", self.room); diff --git a/packages/server/src/args/device_args.rs b/packages/server/src/args/device_args.rs index e9a61c09..38f94020 100644 --- a/packages/server/src/args/device_args.rs +++ b/packages/server/src/args/device_args.rs @@ -19,10 +19,7 @@ impl DeviceArgs { .get_one::("gpu-name") .unwrap_or(&"".to_string()) .clone(), - gpu_index: matches - .get_one::("gpu-index") - .unwrap_or(&-1) - .clone(), + gpu_index: matches.get_one::("gpu-index").unwrap_or(&-1).clone(), gpu_card_path: matches .get_one::("gpu-card-path") .unwrap_or(&"".to_string()) diff --git a/packages/server/src/args/encoding_args.rs b/packages/server/src/args/encoding_args.rs index c0ea478d..d2e8828f 100644 --- a/packages/server/src/args/encoding_args.rs +++ b/packages/server/src/args/encoding_args.rs @@ -120,20 +120,11 @@ impl VideoEncodingOptions { .unwrap(), }), RateControlMethod::CBR => RateControl::CBR(RateControlCBR { - target_bitrate: matches - .get_one::("video-bitrate") - .unwrap() - .clone(), + target_bitrate: matches.get_one::("video-bitrate").unwrap().clone(), }), RateControlMethod::VBR => RateControl::VBR(RateControlVBR { - target_bitrate: matches - .get_one::("video-bitrate") - .unwrap() - .clone(), - max_bitrate: matches - .get_one::("video-bitrate-max") - .unwrap() - .clone(), + target_bitrate: matches.get_one::("video-bitrate").unwrap().clone(), + max_bitrate: matches.get_one::("video-bitrate-max").unwrap().clone(), }), }, }, @@ -209,20 +200,11 @@ impl AudioEncodingOptions { .unwrap_or(&RateControlMethod::CBR) { RateControlMethod::CBR => RateControl::CBR(RateControlCBR { - target_bitrate: matches - .get_one::("audio-bitrate") - .unwrap() - .clone(), + target_bitrate: matches.get_one::("audio-bitrate").unwrap().clone(), }), RateControlMethod::VBR => RateControl::VBR(RateControlVBR { - target_bitrate: matches - .get_one::("audio-bitrate") - .unwrap() - .clone(), - max_bitrate: matches - .get_one::("audio-bitrate-max") - .unwrap() - .clone(), + target_bitrate: matches.get_one::("audio-bitrate").unwrap().clone(), + max_bitrate: matches.get_one::("audio-bitrate-max").unwrap().clone(), }), wot => panic!("Invalid rate control method for audio: {}", wot.as_str()), }, diff --git a/packages/server/src/enc_helper.rs b/packages/server/src/enc_helper.rs index d7be0f8b..3879c8d6 100644 --- a/packages/server/src/enc_helper.rs +++ b/packages/server/src/enc_helper.rs @@ -1,7 +1,7 @@ use crate::args::encoding_args::RateControl; -use crate::gpu::{self, GPUInfo, get_gpu_by_card_path, get_gpus_by_vendor}; +use crate::gpu::{GPUInfo, get_gpu_by_card_path, get_gpus_by_vendor, get_nvidia_gpu_by_cuda_id}; use clap::ValueEnum; -use gst::prelude::*; +use gstreamer::prelude::*; use std::error::Error; use std::str::FromStr; @@ -107,7 +107,7 @@ impl EncoderType { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct VideoEncoderInfo { pub name: String, pub codec: VideoCodec, @@ -146,9 +146,9 @@ impl VideoEncoderInfo { self.parameters.push((key.into(), value.into())); } - pub fn apply_parameters(&self, element: &gst::Element, verbose: bool) { + pub fn apply_parameters(&self, element: &gstreamer::Element, verbose: bool) { for (key, value) in &self.parameters { - if element.has_property(key) { + if element.has_property(key, None) { if verbose { tracing::debug!("Setting property {} to {}", key, value); } @@ -191,7 +191,7 @@ where F: FnMut(&str) -> Option<(String, String)>, { let mut encoder_optz = encoder.clone(); - let element = match gst::ElementFactory::make(&encoder_optz.name).build() { + let element = match gstreamer::ElementFactory::make(&encoder_optz.name).build() { Ok(e) => e, Err(_) => return encoder_optz, // Return original if element creation fails }; @@ -329,16 +329,15 @@ pub fn encoder_low_latency_params( encoder_optz } -pub fn get_compatible_encoders() -> Vec { +pub fn get_compatible_encoders(gpus: &Vec) -> Vec { let mut encoders = Vec::new(); - let registry = gst::Registry::get(); - let gpus = gpu::get_gpus(); + let registry = gstreamer::Registry::get(); for plugin in registry.plugins() { for feature in registry.features_by_plugin(plugin.plugin_name().as_str()) { let encoder_name = feature.name(); - let factory = match gst::ElementFactory::find(encoder_name.as_str()) { + let factory = match gstreamer::ElementFactory::find(encoder_name.as_str()) { Some(f) => f, None => continue, }; @@ -376,9 +375,9 @@ pub fn get_compatible_encoders() -> Vec { match api { EncoderAPI::QSV | EncoderAPI::VAAPI => { // Safe property access with panic protection, gstreamer-rs is fun - let path = if element.has_property("device-path") { + let path = if element.has_property("device-path", None) { Some(element.property::("device-path")) - } else if element.has_property("device") { + } else if element.has_property("device", None) { Some(element.property::("device")) } else { None @@ -386,13 +385,11 @@ pub fn get_compatible_encoders() -> Vec { path.and_then(|p| get_gpu_by_card_path(&gpus, &p)) } - EncoderAPI::NVENC if element.has_property("cuda-device-id") => { + EncoderAPI::NVENC if element.has_property("cuda-device-id", None) => { let cuda_id = element.property::("cuda-device-id"); - get_gpus_by_vendor(&gpus, "nvidia") - .get(cuda_id as usize) - .cloned() + get_nvidia_gpu_by_cuda_id(&gpus, cuda_id as usize) } - EncoderAPI::AMF if element.has_property("device") => { + EncoderAPI::AMF if element.has_property("device", None) => { let device_id = element.property::("device"); get_gpus_by_vendor(&gpus, "amd") .get(device_id as usize) @@ -540,3 +537,140 @@ pub fn get_best_compatible_encoder( Err("No compatible encoder found".into()) } } + +/// Returns the best compatible encoder that also passes test_encoder +pub fn get_best_working_encoder( + encoders: &Vec, + codec: &Codec, + encoder_type: &EncoderType, + dma_buf: bool, +) -> Result> { + let mut candidates = get_encoders_by_videocodec( + encoders, + match codec { + Codec::Video(c) => c, + Codec::Audio(_) => { + return Err("Audio codec not supported for video encoder selection".into()); + } + }, + ); + candidates = get_encoders_by_type(&candidates, encoder_type); + let mut tried = Vec::new(); + while !candidates.is_empty() { + let best = get_best_compatible_encoder(&candidates, codec, encoder_type)?; + tracing::info!("Testing encoder: {}", best.name,); + if test_encoder(&best, dma_buf).is_ok() { + return Ok(best); + } else { + // Remove this encoder and try next best + candidates.retain(|e| e != &best); + tried.push(best.name.clone()); + } + } + Err(format!("No working encoder found (tried: {:?})", tried).into()) +} + +/// Test if a pipeline with the given encoder can be created and set to Playing +pub fn test_encoder(encoder: &VideoEncoderInfo, dma_buf: bool) -> Result<(), Box> { + let src = gstreamer::ElementFactory::make("waylanddisplaysrc").build()?; + if let Some(gpu_info) = &encoder.gpu_info { + src.set_property_from_str("render-node", gpu_info.render_path()); + } + let caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let caps = gstreamer::Caps::from_str(&format!( + "{},width=1280,height=720,framerate=30/1{}", + if dma_buf { + "video/x-raw(memory:DMABuf)" + } else { + "video/x-raw" + }, + if dma_buf { "" } else { ",format=RGBx" } + ))?; + caps_filter.set_property("caps", &caps); + + let enc = gstreamer::ElementFactory::make(&encoder.name).build()?; + let sink = gstreamer::ElementFactory::make("fakesink").build()?; + // Apply encoder parameters + encoder.apply_parameters(&enc, false); + + // Create pipeline and link elements + let pipeline = gstreamer::Pipeline::new(); + + if dma_buf && encoder.encoder_api == EncoderAPI::NVENC { + // GL upload element + let glupload = gstreamer::ElementFactory::make("glupload").build()?; + // GL color convert element + let glconvert = gstreamer::ElementFactory::make("glcolorconvert").build()?; + // GL color convert caps + let gl_caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let gl_caps = gstreamer::Caps::from_str("video/x-raw(memory:GLMemory),format=NV12")?; + gl_caps_filter.set_property("caps", &gl_caps); + // CUDA upload element + let cudaupload = gstreamer::ElementFactory::make("cudaupload").build()?; + + pipeline.add_many(&[ + &src, + &caps_filter, + &glupload, + &glconvert, + &gl_caps_filter, + &cudaupload, + &enc, + &sink, + ])?; + gstreamer::Element::link_many(&[ + &src, + &caps_filter, + &glupload, + &glconvert, + &gl_caps_filter, + &cudaupload, + &enc, + &sink, + ])?; + } else { + let vapostproc = gstreamer::ElementFactory::make("vapostproc").build()?; + // VA caps filter + let va_caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let va_caps = gstreamer::Caps::from_str("video/x-raw(memory:VAMemory),format=NV12")?; + va_caps_filter.set_property("caps", &va_caps); + + pipeline.add_many(&[ + &src, + &caps_filter, + &vapostproc, + &va_caps_filter, + &enc, + &sink, + ])?; + gstreamer::Element::link_many(&[ + &src, + &caps_filter, + &vapostproc, + &va_caps_filter, + &enc, + &sink, + ])?; + } + + let bus = pipeline.bus().ok_or("Pipeline has no bus")?; + let _ = pipeline.set_state(gstreamer::State::Playing); + for msg in bus.iter_timed(gstreamer::ClockTime::from_seconds(2)) { + match msg.view() { + gstreamer::MessageView::Error(err) => { + let err_msg = format!("Pipeline error: {}", err.error()); + tracing::error!("Pipeline error, encoder test failed: {}", err_msg); + let _ = pipeline.set_state(gstreamer::State::Null); + return Err(err_msg.into()); + } + gstreamer::MessageView::Eos(_) => { + tracing::info!("Pipeline EOS received"); + let _ = pipeline.set_state(gstreamer::State::Null); + return Err("Pipeline EOS received, encoder test failed".into()); + } + _ => {} + } + } + let _ = pipeline.set_state(gstreamer::State::Null); + Ok(()) +} diff --git a/packages/server/src/gpu.rs b/packages/server/src/gpu.rs index 8827b0e2..841c3fc2 100644 --- a/packages/server/src/gpu.rs +++ b/packages/server/src/gpu.rs @@ -3,7 +3,7 @@ use std::fs; use std::process::Command; use std::str; -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Hash)] pub enum GPUVendor { UNKNOWN, INTEL, @@ -11,12 +11,13 @@ pub enum GPUVendor { AMD, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct GPUInfo { vendor: GPUVendor, card_path: String, render_path: String, device_name: String, + pci_bus_id: String, } impl GPUInfo { @@ -44,6 +45,10 @@ impl GPUInfo { pub fn device_name(&self) -> &str { &self.device_name } + + pub fn pci_bus_id(&self) -> &str { + &self.pci_bus_id + } } fn get_gpu_vendor(vendor_id: &str) -> GPUVendor { @@ -71,14 +76,17 @@ pub fn get_gpus() -> Vec { .filter(|(class_id, _, _, _)| matches!(class_id.as_str(), "0300" | "0302" | "0380")) .filter_map(|(_, vendor_id, device_name, pci_addr)| { get_dri_device_path(&pci_addr) - .map(|(card, render)| (vendor_id, card, render, device_name)) - }) - .map(|(vid, card_path, render_path, device_name)| GPUInfo { - vendor: get_gpu_vendor(&vid), - card_path, - render_path, - device_name, + .map(|(card, render)| (vendor_id, card, render, device_name, pci_addr)) }) + .map( + |(vid, card_path, render_path, device_name, pci_bus_id)| GPUInfo { + vendor: get_gpu_vendor(&vid), + card_path, + render_path, + device_name, + pci_bus_id, + }, + ) .collect() } @@ -137,7 +145,6 @@ fn get_dri_device_path(pci_addr: &str) -> Option<(String, String)> { None } -// Helper functions remain similar with improved readability: pub fn get_gpus_by_vendor(gpus: &[GPUInfo], vendor: &str) -> Vec { let target = vendor.to_lowercase(); gpus.iter() @@ -162,10 +169,42 @@ pub fn get_gpu_by_card_path(gpus: &[GPUInfo], path: &str) -> Option { .cloned() } -pub fn get_gpu_by_index(gpus: &[GPUInfo], index: i32) -> Option { - if index < 0 || index as usize >= gpus.len() { - None - } else { - Some(gpus[index as usize].clone()) +pub fn get_nvidia_gpu_by_cuda_id(gpus: &[GPUInfo], cuda_device_id: usize) -> Option { + // Check if nvidia-smi is available + if Command::new("nvidia-smi").arg("--help").output().is_err() { + tracing::warn!("nvidia-smi is not available"); + return None; } + + // Run nvidia-smi to get information about the CUDA device + let output = Command::new("nvidia-smi") + .args([ + "--query-gpu=pci.bus_id", + "--format=csv,noheader", + "-i", + &cuda_device_id.to_string(), + ]) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + // Parse the output to get the PCI bus ID + let pci_bus_id = str::from_utf8(&output.stdout).ok()?.trim().to_uppercase(); // nvidia-smi returns uppercase PCI IDs + + // Convert from 00000000:05:00.0 to 05:00.0 if needed + let pci_bus_id = if pci_bus_id.starts_with("00000000:") { + pci_bus_id[9..].to_string() // Skip the domain part + } else if pci_bus_id.starts_with("0000:") { + pci_bus_id[5..].to_string() // Alternate check for older nvidia-smi versions + } else { + pci_bus_id + }; + + // Find the GPU with the matching PCI bus ID + gpus.iter() + .find(|gpu| gpu.vendor == GPUVendor::NVIDIA && gpu.pci_bus_id.to_uppercase() == pci_bus_id) + .cloned() } diff --git a/packages/server/src/main.rs b/packages/server/src/main.rs index be607acb..7a10186a 100644 --- a/packages/server/src/main.rs +++ b/packages/server/src/main.rs @@ -8,24 +8,24 @@ mod p2p; mod proto; use crate::args::encoding_args; -use crate::enc_helper::EncoderType; -use crate::gpu::GPUVendor; +use crate::enc_helper::{EncoderAPI, EncoderType}; +use crate::gpu::{GPUInfo, GPUVendor}; use crate::nestrisink::NestriSignaller; use crate::p2p::p2p::NestriP2P; -use futures_util::StreamExt; -use gst::prelude::*; +use gstreamer::prelude::*; use gstrswebrtc::signaller::Signallable; use gstrswebrtc::webrtcsink::BaseWebRTCSink; use std::error::Error; use std::str::FromStr; use std::sync::Arc; +use tokio_stream::StreamExt; use tracing_subscriber::EnvFilter; use tracing_subscriber::filter::LevelFilter; // Handles gathering GPU information and selecting the most suitable GPU -fn handle_gpus(args: &args::Args) -> Result> { +fn handle_gpus(args: &args::Args) -> Result, Box> { tracing::info!("Gathering GPU information.."); - let gpus = gpu::get_gpus(); + let mut gpus = gpu::get_gpus(); if gpus.is_empty() { return Err("No GPUs found".into()); } @@ -40,10 +40,11 @@ fn handle_gpus(args: &args::Args) -> Result> { ); } - // Based on available arguments, pick a GPU - let gpu; + // Additional GPU filtering if !args.device.gpu_card_path.is_empty() { - gpu = gpu::get_gpu_by_card_path(&gpus, &args.device.gpu_card_path); + if let Some(gpu) = gpu::get_gpu_by_card_path(&gpus, &args.device.gpu_card_path) { + return Ok(Vec::from([gpu])); + } } else { // Run all filters that are not empty let mut filtered_gpus = gpus.clone(); @@ -55,35 +56,43 @@ fn handle_gpus(args: &args::Args) -> Result> { } if args.device.gpu_index > -1 { // get single GPU by index - gpu = gpu::get_gpu_by_index(&filtered_gpus, args.device.gpu_index).or_else(|| { - tracing::warn!("GPU index {} is out of range", args.device.gpu_index); - None - }); + let gpu_index = args.device.gpu_index as usize; + if gpu_index >= filtered_gpus.len() { + return Err(format!( + "GPU index {} is out of bounds for available GPUs (0-{})", + gpu_index, + filtered_gpus.len() - 1 + ) + .into()); + } + gpus = Vec::from([filtered_gpus[gpu_index].clone()]); } else { - // get first GPU - gpu = filtered_gpus + // Filter out unknown vendor GPUs + gpus = filtered_gpus .into_iter() - .find(|g| *g.vendor() != GPUVendor::UNKNOWN); + .filter(|gpu| *gpu.vendor() != GPUVendor::UNKNOWN) + .collect(); } } - if gpu.is_none() { + if gpus.is_empty() { return Err(format!( - "No GPU found with the specified parameters: vendor='{}', name='{}', index='{}', card_path='{}'", + "No GPU(s) found with the specified parameters: vendor='{}', name='{}', index='{}', card_path='{}'", args.device.gpu_vendor, args.device.gpu_name, args.device.gpu_index, args.device.gpu_card_path ).into()); } - let gpu = gpu.unwrap(); - tracing::info!("Selected GPU: '{}'", gpu.device_name()); - Ok(gpu) + Ok(gpus) } // Handles picking video encoder -fn handle_encoder_video(args: &args::Args) -> Result> { +fn handle_encoder_video( + args: &args::Args, + gpus: &Vec, +) -> Result> { tracing::info!("Getting compatible video encoders.."); - let video_encoders = enc_helper::get_compatible_encoders(); + let video_encoders = enc_helper::get_compatible_encoders(gpus); if video_encoders.is_empty() { return Err("No compatible video encoders found".into()); } @@ -107,10 +116,11 @@ fn handle_encoder_video(args: &args::Args) -> Result Result<(), Box> { let nestri_p2p = Arc::new(NestriP2P::new().await?); let p2p_conn = nestri_p2p.connect(relay_url).await?; - gst::init()?; - gstrswebrtc::plugin_register_static()?; - - // Handle GPU selection - let gpu = match handle_gpus(&args) { - Ok(gpu) => gpu, - Err(e) => { - tracing::error!("Failed to find a suitable GPU: {}", e); - return Err(e); - } - }; + gstreamer::init()?; + let _ = gstrswebrtc::plugin_register_static(); // Might be already registered, so we'll pass.. if args.app.dma_buf { if args.encoding.video.encoder_type != EncoderType::HARDWARE { @@ -214,8 +215,17 @@ async fn main() -> Result<(), Box> { } } + // Handle GPU selection + let gpus = match handle_gpus(&args) { + Ok(gpu) => gpu, + Err(e) => { + tracing::error!("Failed to find a suitable GPU: {}", e); + return Err(e); + } + }; + // Handle video encoder selection - let mut video_encoder_info = match handle_encoder_video(&args) { + let mut video_encoder_info = match handle_encoder_video(&args, &gpus) { Ok(encoder) => encoder, Err(e) => { tracing::error!("Failed to find a suitable video encoder: {}", e); @@ -231,33 +241,35 @@ async fn main() -> Result<(), Box> { /*** PIPELINE CREATION ***/ // Create the pipeline - let pipeline = Arc::new(gst::Pipeline::new()); + let pipeline = Arc::new(gstreamer::Pipeline::new()); /* Audio */ // Audio Source Element let audio_source = match args.encoding.audio.capture_method { encoding_args::AudioCaptureMethod::PULSEAUDIO => { - gst::ElementFactory::make("pulsesrc").build()? + gstreamer::ElementFactory::make("pulsesrc").build()? } encoding_args::AudioCaptureMethod::PIPEWIRE => { - gst::ElementFactory::make("pipewiresrc").build()? + gstreamer::ElementFactory::make("pipewiresrc").build()? + } + encoding_args::AudioCaptureMethod::ALSA => { + gstreamer::ElementFactory::make("alsasrc").build()? } - encoding_args::AudioCaptureMethod::ALSA => gst::ElementFactory::make("alsasrc").build()?, }; // Audio Converter Element - let audio_converter = gst::ElementFactory::make("audioconvert").build()?; + let audio_converter = gstreamer::ElementFactory::make("audioconvert").build()?; // Audio Rate Element - let audio_rate = gst::ElementFactory::make("audiorate").build()?; + let audio_rate = gstreamer::ElementFactory::make("audiorate").build()?; // Required to fix gstreamer opus issue, where quality sounds off (due to wrong sample rate) - let audio_capsfilter = gst::ElementFactory::make("capsfilter").build()?; - let audio_caps = gst::Caps::from_str("audio/x-raw,rate=48000,channels=2").unwrap(); + let audio_capsfilter = gstreamer::ElementFactory::make("capsfilter").build()?; + let audio_caps = gstreamer::Caps::from_str("audio/x-raw,rate=48000,channels=2").unwrap(); audio_capsfilter.set_property("caps", &audio_caps); // Audio Encoder Element - let audio_encoder = gst::ElementFactory::make(audio_encoder.as_str()).build()?; + let audio_encoder = gstreamer::ElementFactory::make(audio_encoder.as_str()).build()?; audio_encoder.set_property( "bitrate", &match &args.encoding.audio.rate_control { @@ -267,18 +279,27 @@ async fn main() -> Result<(), Box> { }, ); // If has "frame-size" (opus), set to 10 for lower latency (below 10 seems to be too low?) - if audio_encoder.has_property("frame-size") { + if audio_encoder.has_property("frame-size", None) { audio_encoder.set_property_from_str("frame-size", "10"); } + // Audio parse Element + let mut audio_parser = None; + if audio_encoder.name() == "opusenc" { + // Opus encoder requires a parser + audio_parser = Some(gstreamer::ElementFactory::make("opusparse").build()?); + } + /* Video */ // Video Source Element - let video_source = Arc::new(gst::ElementFactory::make("waylanddisplaysrc").build()?); - video_source.set_property_from_str("render-node", gpu.render_path()); + let video_source = Arc::new(gstreamer::ElementFactory::make("waylanddisplaysrc").build()?); + if let Some(gpu_info) = &video_encoder_info.gpu_info { + video_source.set_property_from_str("render-node", gpu_info.render_path()); + } // Caps Filter Element (resolution, fps) - let caps_filter = gst::ElementFactory::make("capsfilter").build()?; - let caps = gst::Caps::from_str(&format!( + let caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let caps = gstreamer::Caps::from_str(&format!( "{},width={},height={},framerate={}/1{}", if args.app.dma_buf { "video/x-raw(memory:DMABuf)" @@ -292,37 +313,70 @@ async fn main() -> Result<(), Box> { ))?; caps_filter.set_property("caps", &caps); - // GL Upload element - let glupload = gst::ElementFactory::make("glupload").build()?; + // GL and CUDA elements (NVIDIA only..) + let mut glupload = None; + let mut glconvert = None; + let mut gl_caps_filter = None; + let mut cudaupload = None; + if args.app.dma_buf && video_encoder_info.encoder_api == EncoderAPI::NVENC { + // GL upload element + glupload = Some(gstreamer::ElementFactory::make("glupload").build()?); + // GL color convert element + glconvert = Some(gstreamer::ElementFactory::make("glcolorconvert").build()?); + // GL color convert caps + let caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let gl_caps = gstreamer::Caps::from_str("video/x-raw(memory:GLMemory),format=NV12")?; + caps_filter.set_property("caps", &gl_caps); + gl_caps_filter = Some(caps_filter); + // CUDA upload element + cudaupload = Some(gstreamer::ElementFactory::make("cudaupload").build()?); + } - // GL color convert element - let glcolorconvert = gst::ElementFactory::make("glcolorconvert").build()?; - - // GL upload caps filter - let gl_caps_filter = gst::ElementFactory::make("capsfilter").build()?; - let gl_caps = gst::Caps::from_str("video/x-raw(memory:GLMemory),format=NV12")?; - gl_caps_filter.set_property("caps", &gl_caps); - - // GL download element (needed only for DMA-BUF outside NVIDIA GPUs) - let gl_download = gst::ElementFactory::make("gldownload").build()?; + // vapostproc for VA compatible encoders + let mut vapostproc = None; + let mut va_caps_filter = None; + if video_encoder_info.encoder_api == EncoderAPI::VAAPI + || video_encoder_info.encoder_api == EncoderAPI::QSV + { + vapostproc = Some(gstreamer::ElementFactory::make("vapostproc").build()?); + // VA caps filter + let caps_filter = gstreamer::ElementFactory::make("capsfilter").build()?; + let va_caps = gstreamer::Caps::from_str("video/x-raw(memory:VAMemory),format=NV12")?; + caps_filter.set_property("caps", &va_caps); + va_caps_filter = Some(caps_filter); + } // Video Converter Element - let video_converter = gst::ElementFactory::make("videoconvert").build()?; + let mut video_converter = None; + if !args.app.dma_buf { + video_converter = Some(gstreamer::ElementFactory::make("videoconvert").build()?); + } // Video Encoder Element - let video_encoder = gst::ElementFactory::make(video_encoder_info.name.as_str()).build()?; + let video_encoder = + gstreamer::ElementFactory::make(video_encoder_info.name.as_str()).build()?; video_encoder_info.apply_parameters(&video_encoder, args.app.verbose); - // Video parser Element, required for GStreamer 1.26 as it broke some things.. + // Video parser Element let video_parser; - if video_encoder_info.codec == enc_helper::VideoCodec::H264 { - video_parser = Some( - gst::ElementFactory::make("h264parse") - .property("config-interval", -1i32) - .build()?, - ); - } else { - video_parser = None; + match video_encoder_info.codec { + enc_helper::VideoCodec::H264 => { + video_parser = Some( + gstreamer::ElementFactory::make("h264parse") + .property("config-interval", -1i32) + .build()?, + ); + } + enc_helper::VideoCodec::H265 => { + video_parser = Some( + gstreamer::ElementFactory::make("h265parse") + .property("config-interval", -1i32) + .build()?, + ); + } + _ => { + video_parser = None; + } } /* Output */ @@ -335,24 +389,24 @@ async fn main() -> Result<(), Box> { webrtcsink.set_property("do-retransmission", false); /* Queues */ - let video_queue = gst::ElementFactory::make("queue2") + let video_queue = gstreamer::ElementFactory::make("queue2") .property("max-size-buffers", 3u32) .property("max-size-time", 0u64) .property("max-size-bytes", 0u32) .build()?; - let audio_queue = gst::ElementFactory::make("queue2") + let audio_queue = gstreamer::ElementFactory::make("queue2") .property("max-size-buffers", 3u32) .property("max-size-time", 0u64) .property("max-size-bytes", 0u32) .build()?; /* Clock Sync */ - let video_clocksync = gst::ElementFactory::make("clocksync") + let video_clocksync = gstreamer::ElementFactory::make("clocksync") .property("sync-to-first", true) .build()?; - let audio_clocksync = gst::ElementFactory::make("clocksync") + let audio_clocksync = gstreamer::ElementFactory::make("clocksync") .property("sync-to-first", true) .build()?; @@ -360,7 +414,6 @@ async fn main() -> Result<(), Box> { pipeline.add_many(&[ webrtcsink.upcast_ref(), &video_encoder, - &video_converter, &caps_filter, &video_queue, &video_clocksync, @@ -374,21 +427,35 @@ async fn main() -> Result<(), Box> { &audio_source, ])?; + if let Some(video_converter) = &video_converter { + pipeline.add(video_converter)?; + } + + if let Some(parser) = &audio_parser { + pipeline.add(parser)?; + } + if let Some(parser) = &video_parser { pipeline.add(parser)?; } - // If DMA-BUF is enabled, add glupload, color conversion and caps filter + // If DMA-BUF.. if args.app.dma_buf { - if *gpu.vendor() == GPUVendor::NVIDIA { - pipeline.add_many(&[&glupload, &glcolorconvert, &gl_caps_filter])?; + // VA-API / QSV pipeline + if let (Some(vapostproc), Some(va_caps_filter)) = (&vapostproc, &va_caps_filter) { + pipeline.add_many(&[vapostproc, va_caps_filter])?; } else { - pipeline.add_many(&[&glupload, &glcolorconvert, &gl_caps_filter, &gl_download])?; + // NVENC pipeline + if let (Some(glupload), Some(glconvert), Some(gl_caps_filter), Some(cudaupload)) = + (&glupload, &glconvert, &gl_caps_filter, &cudaupload) + { + pipeline.add_many(&[glupload, glconvert, gl_caps_filter, cudaupload])?; + } } } // Link main audio branch - gst::Element::link_many(&[ + gstreamer::Element::link_many(&[ &audio_source, &audio_converter, &audio_rate, @@ -396,51 +463,62 @@ async fn main() -> Result<(), Box> { &audio_queue, &audio_clocksync, &audio_encoder, - webrtcsink.upcast_ref(), ])?; - // With DMA-BUF, also link glupload and it's caps + // Link audio parser to audio encoder if present, otherwise just webrtcsink + if let Some(parser) = &audio_parser { + gstreamer::Element::link_many(&[&audio_encoder, parser, webrtcsink.upcast_ref()])?; + } else { + gstreamer::Element::link_many(&[&audio_encoder, webrtcsink.upcast_ref()])?; + } + + // With DMA-BUF.. if args.app.dma_buf { - if *gpu.vendor() == GPUVendor::NVIDIA { - gst::Element::link_many(&[ + // VA-API / QSV pipeline + if let (Some(vapostproc), Some(va_caps_filter)) = (&vapostproc, &va_caps_filter) { + gstreamer::Element::link_many(&[ &video_source, &caps_filter, &video_queue, &video_clocksync, - &glupload, - &glcolorconvert, - &gl_caps_filter, + &vapostproc, + &va_caps_filter, &video_encoder, ])?; } else { - gst::Element::link_many(&[ - &video_source, - &caps_filter, - &video_queue, - &video_clocksync, - &glupload, - &glcolorconvert, - &gl_caps_filter, - &gl_download, - &video_encoder, - ])?; + // NVENC pipeline + if let (Some(glupload), Some(glconvert), Some(gl_caps_filter), Some(cudaupload)) = + (&glupload, &glconvert, &gl_caps_filter, &cudaupload) + { + gstreamer::Element::link_many(&[ + &video_source, + &caps_filter, + &video_queue, + &video_clocksync, + &glupload, + &glconvert, + &gl_caps_filter, + &cudaupload, + &video_encoder, + ])?; + } } } else { - gst::Element::link_many(&[ + gstreamer::Element::link_many(&[ &video_source, &caps_filter, &video_queue, &video_clocksync, - &video_converter, + &video_converter.unwrap(), &video_encoder, ])?; } // Link video parser if present with webrtcsink, otherwise just link webrtc sink if let Some(parser) = &video_parser { - gst::Element::link_many(&[&video_encoder, parser, webrtcsink.upcast_ref()])?; + gstreamer::Element::link_many(&[&video_encoder, parser, webrtcsink.upcast_ref()])?; } else { - gst::Element::link_many(&[&video_encoder, webrtcsink.upcast_ref()])?; + gstreamer::Element::link_many(&[&video_encoder, webrtcsink.upcast_ref()])?; } // Set QOS @@ -468,14 +546,17 @@ async fn main() -> Result<(), Box> { } } + // Clean up + tracing::info!("Exiting gracefully.."); + Ok(()) } -async fn run_pipeline(pipeline: Arc) -> Result<(), Box> { +async fn run_pipeline(pipeline: Arc) -> Result<(), Box> { let bus = { pipeline.bus().ok_or("Pipeline has no bus")? }; { - if let Err(e) = pipeline.set_state(gst::State::Playing) { + if let Err(e) = pipeline.set_state(gstreamer::State::Playing) { tracing::error!("Failed to start pipeline: {}", e); return Err("Failed to start pipeline".into()); } @@ -495,24 +576,24 @@ async fn run_pipeline(pipeline: Arc) -> Result<(), Box } { - pipeline.set_state(gst::State::Null)?; + pipeline.set_state(gstreamer::State::Null)?; } Ok(()) } -async fn listen_for_gst_messages(bus: gst::Bus) -> Result<(), Box> { +async fn listen_for_gst_messages(bus: gstreamer::Bus) -> Result<(), Box> { let bus_stream = bus.stream(); tokio::pin!(bus_stream); while let Some(msg) = bus_stream.next().await { match msg.view() { - gst::MessageView::Eos(_) => { + gstreamer::MessageView::Eos(_) => { tracing::info!("Received EOS"); break; } - gst::MessageView::Error(err) => { + gstreamer::MessageView::Error(err) => { let err_msg = format!( "Error from {:?}: {:?}", err.src().map(|s| s.path_string()), diff --git a/packages/server/src/nestrisink/imp.rs b/packages/server/src/nestrisink/imp.rs index 0309b872..9d98c82f 100644 --- a/packages/server/src/nestrisink/imp.rs +++ b/packages/server/src/nestrisink/imp.rs @@ -7,9 +7,9 @@ use crate::proto::proto::proto_input::InputType::{ use crate::proto::proto::{ProtoInput, ProtoMessageInput}; use atomic_refcell::AtomicRefCell; use glib::subclass::prelude::*; -use gst::glib; -use gst::prelude::*; -use gst_webrtc::{WebRTCSDPType, WebRTCSessionDescription, gst_sdp}; +use gstreamer::glib; +use gstreamer::prelude::*; +use gstreamer_webrtc::{gst_sdp, WebRTCSDPType, WebRTCSessionDescription}; use gstrswebrtc::signaller::{Signallable, SignallableImpl}; use parking_lot::RwLock as PLRwLock; use prost::Message; @@ -20,8 +20,8 @@ use webrtc::peer_connection::sdp::session_description::RTCSessionDescription; pub struct Signaller { stream_room: PLRwLock>, stream_protocol: PLRwLock>>, - wayland_src: PLRwLock>>, - data_channel: AtomicRefCell>, + wayland_src: PLRwLock>>, + data_channel: AtomicRefCell>, } impl Default for Signaller { fn default() -> Self { @@ -51,19 +51,19 @@ impl Signaller { self.stream_protocol.read().clone() } - pub fn set_wayland_src(&self, wayland_src: Arc) { + pub fn set_wayland_src(&self, wayland_src: Arc) { *self.wayland_src.write() = Some(wayland_src); } - pub fn get_wayland_src(&self) -> Option> { + pub fn get_wayland_src(&self) -> Option> { self.wayland_src.read().clone() } - pub fn set_data_channel(&self, data_channel: gst_webrtc::WebRTCDataChannel) { + pub fn set_data_channel(&self, data_channel: gstreamer_webrtc::WebRTCDataChannel) { match self.data_channel.try_borrow_mut() { Ok(mut dc) => *dc = Some(data_channel), - Err(_) => gst::warning!( - gst::CAT_DEFAULT, + Err(_) => gstreamer::warning!( + gstreamer::CAT_DEFAULT, "Failed to set data channel - already borrowed" ), } @@ -72,7 +72,7 @@ impl Signaller { /// Helper method to clean things up fn register_callbacks(&self) { let Some(stream_protocol) = self.get_stream_protocol() else { - gst::error!(gst::CAT_DEFAULT, "Stream protocol not set"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Stream protocol not set"); return; }; { @@ -87,7 +87,7 @@ impl Signaller { &[&"unique-session-id", &answer], ); } else { - gst::error!(gst::CAT_DEFAULT, "Failed to decode SDP message"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Failed to decode SDP message"); } }); } @@ -108,7 +108,7 @@ impl Signaller { ], ); } else { - gst::error!(gst::CAT_DEFAULT, "Failed to decode ICE message"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Failed to decode ICE message"); } }); } @@ -118,13 +118,16 @@ impl Signaller { if let Ok(answer) = serde_json::from_slice::(&data) { // Decode room name string if let Some(room_name) = answer.data.as_str() { - gst::info!( - gst::CAT_DEFAULT, + gstreamer::info!( + gstreamer::CAT_DEFAULT, "Received OK answer for room: {}", room_name ); } else { - gst::error!(gst::CAT_DEFAULT, "Failed to decode room name from answer"); + gstreamer::error!( + gstreamer::CAT_DEFAULT, + "Failed to decode room name from answer" + ); } // Send our SDP offer @@ -137,7 +140,7 @@ impl Signaller { ], ); } else { - gst::error!(gst::CAT_DEFAULT, "Failed to decode answer"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Failed to decode answer"); } }); } @@ -147,44 +150,52 @@ impl Signaller { self_obj.connect_closure( "webrtcbin-ready", false, - glib::closure!(move |signaller: &super::NestriSignaller, - _consumer_identifier: &str, - webrtcbin: &gst::Element| { - gst::info!(gst::CAT_DEFAULT, "Adding data channels"); - // Create data channels on webrtcbin - let data_channel = Some( - webrtcbin.emit_by_name::( - "create-data-channel", - &[ - &"nestri-data-channel", - &gst::Structure::builder("config") - .field("ordered", &true) - .field("max-retransmits", &2u32) - .field("priority", "high") - .field("protocol", "raw") - .build(), - ], - ), - ); - if let Some(data_channel) = data_channel { - gst::info!(gst::CAT_DEFAULT, "Data channel created"); - if let Some(wayland_src) = signaller.imp().get_wayland_src() { - setup_data_channel(&data_channel, &*wayland_src); - signaller.imp().set_data_channel(data_channel); + glib::closure!( + move |signaller: &super::NestriSignaller, + _consumer_identifier: &str, + webrtcbin: &gstreamer::Element| { + gstreamer::info!(gstreamer::CAT_DEFAULT, "Adding data channels"); + // Create data channels on webrtcbin + let data_channel = Some( + webrtcbin.emit_by_name::( + "create-data-channel", + &[ + &"nestri-data-channel", + &gstreamer::Structure::builder("config") + .field("ordered", &true) + .field("max-retransmits", &2u32) + .field("priority", "high") + .field("protocol", "raw") + .build(), + ], + ), + ); + if let Some(data_channel) = data_channel { + gstreamer::info!(gstreamer::CAT_DEFAULT, "Data channel created"); + if let Some(wayland_src) = signaller.imp().get_wayland_src() { + setup_data_channel(&data_channel, &*wayland_src); + signaller.imp().set_data_channel(data_channel); + } else { + gstreamer::error!( + gstreamer::CAT_DEFAULT, + "Wayland display source not set" + ); + } } else { - gst::error!(gst::CAT_DEFAULT, "Wayland display source not set"); + gstreamer::error!( + gstreamer::CAT_DEFAULT, + "Failed to create data channel" + ); } - } else { - gst::error!(gst::CAT_DEFAULT, "Failed to create data channel"); } - }), + ), ); } } } impl SignallableImpl for Signaller { fn start(&self) { - gst::info!(gst::CAT_DEFAULT, "Signaller started"); + gstreamer::info!(gstreamer::CAT_DEFAULT, "Signaller started"); // Register message callbacks self.register_callbacks(); @@ -193,7 +204,7 @@ impl SignallableImpl for Signaller { // TODO: Re-implement reconnection handling let Some(stream_room) = self.stream_room.read().clone() else { - gst::error!(gst::CAT_DEFAULT, "Stream room not set"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Stream room not set"); return; }; @@ -206,7 +217,7 @@ impl SignallableImpl for Signaller { }; let Some(stream_protocol) = self.get_stream_protocol() else { - gst::error!(gst::CAT_DEFAULT, "Stream protocol not set"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Stream protocol not set"); return; }; @@ -216,7 +227,7 @@ impl SignallableImpl for Signaller { } fn stop(&self) { - gst::info!(gst::CAT_DEFAULT, "Signaller stopped"); + gstreamer::info!(gstreamer::CAT_DEFAULT, "Signaller stopped"); } fn send_sdp(&self, _session_id: &str, sdp: &WebRTCSessionDescription) { @@ -229,7 +240,7 @@ impl SignallableImpl for Signaller { }; let Some(stream_protocol) = self.get_stream_protocol() else { - gst::error!(gst::CAT_DEFAULT, "Stream protocol not set"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Stream protocol not set"); return; }; @@ -260,7 +271,7 @@ impl SignallableImpl for Signaller { }; let Some(stream_protocol) = self.get_stream_protocol() else { - gst::error!(gst::CAT_DEFAULT, "Stream protocol not set"); + gstreamer::error!(gstreamer::CAT_DEFAULT, "Stream protocol not set"); return; }; @@ -270,7 +281,7 @@ impl SignallableImpl for Signaller { } fn end_session(&self, session_id: &str) { - gst::info!(gst::CAT_DEFAULT, "Ending session: {}", session_id); + gstreamer::info!(gstreamer::CAT_DEFAULT, "Ending session: {}", session_id); } } #[glib::object_subclass] @@ -303,7 +314,10 @@ impl ObjectImpl for Signaller { } } -fn setup_data_channel(data_channel: &gst_webrtc::WebRTCDataChannel, wayland_src: &gst::Element) { +fn setup_data_channel( + data_channel: &gstreamer_webrtc::WebRTCDataChannel, + wayland_src: &gstreamer::Element, +) { let wayland_src = wayland_src.clone(); data_channel.connect_on_message_data(move |_data_channel, data| { @@ -328,64 +342,64 @@ fn setup_data_channel(data_channel: &gst_webrtc::WebRTCDataChannel, wayland_src: }); } -fn handle_input_message(input_msg: ProtoInput) -> Option { +fn handle_input_message(input_msg: ProtoInput) -> Option { if let Some(input_type) = input_msg.input_type { match input_type { MouseMove(data) => { - let structure = gst::Structure::builder("MouseMoveRelative") + let structure = gstreamer::Structure::builder("MouseMoveRelative") .field("pointer_x", data.x as f64) .field("pointer_y", data.y as f64) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } MouseMoveAbs(data) => { - let structure = gst::Structure::builder("MouseMoveAbsolute") + let structure = gstreamer::Structure::builder("MouseMoveAbsolute") .field("pointer_x", data.x as f64) .field("pointer_y", data.y as f64) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } KeyDown(data) => { - let structure = gst::Structure::builder("KeyboardKey") + let structure = gstreamer::Structure::builder("KeyboardKey") .field("key", data.key as u32) .field("pressed", true) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } KeyUp(data) => { - let structure = gst::Structure::builder("KeyboardKey") + let structure = gstreamer::Structure::builder("KeyboardKey") .field("key", data.key as u32) .field("pressed", false) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } MouseWheel(data) => { - let structure = gst::Structure::builder("MouseAxis") + let structure = gstreamer::Structure::builder("MouseAxis") .field("x", data.x as f64) .field("y", data.y as f64) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } MouseKeyDown(data) => { - let structure = gst::Structure::builder("MouseButton") + let structure = gstreamer::Structure::builder("MouseButton") .field("button", data.key as u32) .field("pressed", true) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } MouseKeyUp(data) => { - let structure = gst::Structure::builder("MouseButton") + let structure = gstreamer::Structure::builder("MouseButton") .field("button", data.key as u32) .field("pressed", false) .build(); - Some(gst::event::CustomUpstream::new(structure)) + Some(gstreamer::event::CustomUpstream::new(structure)) } } } else { diff --git a/packages/server/src/nestrisink/mod.rs b/packages/server/src/nestrisink/mod.rs index b9692d0a..122c2aeb 100644 --- a/packages/server/src/nestrisink/mod.rs +++ b/packages/server/src/nestrisink/mod.rs @@ -1,6 +1,6 @@ use crate::p2p::p2p::NestriConnection; -use gst::glib; -use gst::subclass::prelude::*; +use gstreamer::glib; +use gstreamer::subclass::prelude::*; use gstrswebrtc::signaller::Signallable; use std::sync::Arc; @@ -14,7 +14,7 @@ impl NestriSignaller { pub async fn new( room: String, nestri_conn: NestriConnection, - wayland_src: Arc, + wayland_src: Arc, ) -> Result> { let obj: Self = glib::Object::new(); obj.imp().set_stream_room(room); diff --git a/packages/server/src/p2p/p2p.rs b/packages/server/src/p2p/p2p.rs index 0b1ca793..a9d66f9f 100644 --- a/packages/server/src/p2p/p2p.rs +++ b/packages/server/src/p2p/p2p.rs @@ -1,4 +1,4 @@ -use futures_util::StreamExt; +use libp2p::futures::StreamExt; use libp2p::multiaddr::Protocol; use libp2p::{ Multiaddr, PeerId, Swarm, identify, noise, ping, @@ -20,6 +20,7 @@ struct NestriBehaviour { identify: identify::Behaviour, ping: ping::Behaviour, stream: libp2p_stream::Behaviour, + autonatv2: libp2p::autonat::v2::client::Behaviour, } pub struct NestriP2P { @@ -36,6 +37,8 @@ impl NestriP2P { yamux::Config::default, )? .with_dns()? + .with_websocket(noise::Config::new, yamux::Config::default) + .await? .with_behaviour(|key| { let identify_behaviour = identify::Behaviour::new(identify::Config::new( "/ipfs/id/1.0.0".to_string(), @@ -43,11 +46,13 @@ impl NestriP2P { )); let ping_behaviour = ping::Behaviour::default(); let stream_behaviour = libp2p_stream::Behaviour::default(); + let autonatv2_behaviour = libp2p::autonat::v2::client::Behaviour::default(); Ok(NestriBehaviour { identify: identify_behaviour, ping: ping_behaviour, stream: stream_behaviour, + autonatv2: autonatv2_behaviour, }) })? .build(), diff --git a/packages/server/src/p2p/p2p_protocol_stream.rs b/packages/server/src/p2p/p2p_protocol_stream.rs index b47c769a..15aa759d 100644 --- a/packages/server/src/p2p/p2p_protocol_stream.rs +++ b/packages/server/src/p2p/p2p_protocol_stream.rs @@ -1,9 +1,10 @@ use crate::p2p::p2p::NestriConnection; use crate::p2p::p2p_safestream::SafeStream; +use dashmap::DashMap; use libp2p::StreamProtocol; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use tokio::sync::mpsc; +use tokio::time::{self, Duration}; // Cloneable callback type pub type CallbackInner = dyn Fn(Vec) + Send + Sync + 'static; @@ -33,9 +34,11 @@ impl From> for Callback { /// NestriStreamProtocol manages the stream protocol for Nestri connections. pub struct NestriStreamProtocol { - tx: mpsc::Sender>, + tx: Option>>, safe_stream: Arc, - callbacks: Arc>>, + callbacks: Arc>, + read_handle: Option>, + write_handle: Option>, } impl NestriStreamProtocol { const NESTRI_PROTOCOL_STREAM_PUSH: StreamProtocol = @@ -56,21 +59,35 @@ impl NestriStreamProtocol { } }; - let (tx, rx) = mpsc::channel(1000); - - let sp = NestriStreamProtocol { - tx, + let mut sp = NestriStreamProtocol { + tx: None, safe_stream: Arc::new(SafeStream::new(push_stream)), - callbacks: Arc::new(RwLock::new(HashMap::new())), + callbacks: Arc::new(DashMap::new()), + read_handle: None, + write_handle: None, }; - // Spawn the loops - sp.spawn_read_loop(); - sp.spawn_write_loop(rx); + // Use restart method to initialize the read and write loops + sp.restart()?; Ok(sp) } + pub fn restart(&mut self) -> Result<(), Box> { + // Return if tx and handles are already initialized + if self.tx.is_some() && self.read_handle.is_some() && self.write_handle.is_some() { + tracing::warn!("NestriStreamProtocol is already running, restart skipped"); + return Ok(()); + } + + let (tx, rx) = mpsc::channel(1000); + self.tx = Some(tx); + self.read_handle = Some(self.spawn_read_loop()); + self.write_handle = Some(self.spawn_write_loop(rx)); + + Ok(()) + } + fn spawn_read_loop(&self) -> tokio::task::JoinHandle<()> { let safe_stream = self.safe_stream.clone(); let callbacks = self.callbacks.clone(); @@ -89,14 +106,22 @@ impl NestriStreamProtocol { match serde_json::from_slice::(&data) { Ok(base_message) => { let response_type = base_message.payload_type; - let callback = { - let callbacks_lock = callbacks.read().unwrap(); - callbacks_lock.get(&response_type).cloned() - }; - if let Some(callback) = callback { - // Call the registered callback with the raw data - callback.call(data); + // With DashMap, we don't need explicit locking + // we just get the callback directly if it exists + if let Some(callback) = callbacks.get(&response_type) { + // Execute the callback + if let Err(e) = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + callback.call(data.clone()) + })) + { + tracing::error!( + "Callback for response type '{}' panicked: {:?}", + response_type, + e + ); + } } else { tracing::warn!( "No callback registered for response type: {}", @@ -108,6 +133,9 @@ impl NestriStreamProtocol { tracing::error!("Failed to decode message: {}", e); } } + + // Add a small sleep to reduce CPU usage + time::sleep(Duration::from_micros(100)).await; } }) } @@ -117,14 +145,20 @@ impl NestriStreamProtocol { tokio::spawn(async move { loop { // Wait for a message from the channel - if let Some(tx_data) = rx.recv().await { - if let Err(e) = safe_stream.send_raw(&tx_data).await { - tracing::error!("Error sending data: {:?}", e); + match rx.recv().await { + Some(tx_data) => { + if let Err(e) = safe_stream.send_raw(&tx_data).await { + tracing::error!("Error sending data: {:?}", e); + } + } + None => { + tracing::info!("Receiver closed, exiting write loop"); + break; } - } else { - tracing::info!("Receiver closed, exiting write loop"); - break; } + + // Add a small sleep to reduce CPU usage + time::sleep(Duration::from_micros(100)).await; } }) } @@ -134,16 +168,25 @@ impl NestriStreamProtocol { message: &M, ) -> Result<(), Box> { let json_data = serde_json::to_vec(message)?; - self.tx.try_send(json_data)?; + let Some(tx) = &self.tx else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotConnected, + if self.read_handle.is_none() && self.write_handle.is_none() { + "NestriStreamProtocol has been shutdown" + } else { + "NestriStreamProtocol is not properly initialized" + }, + ))); + }; + tx.try_send(json_data)?; Ok(()) } - /// Register a callback for a specific response type pub fn register_callback(&self, response_type: &str, callback: F) where F: Fn(Vec) + Send + Sync + 'static, { - let mut callbacks_lock = self.callbacks.write().unwrap(); - callbacks_lock.insert(response_type.to_string(), Callback::new(callback)); + self.callbacks + .insert(response_type.to_string(), Callback::new(callback)); } } diff --git a/packages/server/src/p2p/p2p_safestream.rs b/packages/server/src/p2p/p2p_safestream.rs index 6fa74ca5..f243be21 100644 --- a/packages/server/src/p2p/p2p_safestream.rs +++ b/packages/server/src/p2p/p2p_safestream.rs @@ -1,6 +1,6 @@ use byteorder::{BigEndian, ByteOrder}; -use futures_util::io::{ReadHalf, WriteHalf}; -use futures_util::{AsyncReadExt, AsyncWriteExt}; +use libp2p::futures::io::{ReadHalf, WriteHalf}; +use libp2p::futures::{AsyncReadExt, AsyncWriteExt}; use prost::Message; use serde::Serialize; use serde::de::DeserializeOwned; @@ -63,21 +63,15 @@ impl SafeStream { async fn send_with_length_prefix(&self, data: &[u8]) -> Result<(), Box> { if data.len() > MAX_SIZE { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Data exceeds maximum size", - ))); + return Err("Data exceeds maximum size".into()); } + let mut buffer = Vec::with_capacity(4 + data.len()); + buffer.extend_from_slice(&(data.len() as u32).to_be_bytes()); // Length prefix + buffer.extend_from_slice(data); // Payload + let mut stream_write = self.stream_write.lock().await; - - // Write the 4-byte length prefix - let mut length_prefix = [0u8; 4]; - BigEndian::write_u32(&mut length_prefix, data.len() as u32); - stream_write.write_all(&length_prefix).await?; - - // Write the actual data - stream_write.write_all(data).await?; + stream_write.write_all(&buffer).await?; // Single write stream_write.flush().await?; Ok(()) } @@ -85,20 +79,16 @@ impl SafeStream { async fn receive_with_length_prefix(&self) -> Result, Box> { let mut stream_read = self.stream_read.lock().await; - // Read the 4-byte length prefix + // Read length prefix + data in one syscall let mut length_prefix = [0u8; 4]; stream_read.read_exact(&mut length_prefix).await?; let length = BigEndian::read_u32(&length_prefix) as usize; if length > MAX_SIZE { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Data exceeds maximum size", - ))); + return Err("Data exceeds maximum size".into()); } - // Read the actual data - let mut buffer = vec![0; length]; + let mut buffer = vec![0u8; length]; stream_read.read_exact(&mut buffer).await?; Ok(buffer) }