From 8d0233c61d53b18bd1befd4102fb8d823737de08 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 16 Sep 2026 20:10:36 -0700 Subject: [PATCH 1/4] Lazily init cuda --- .../src/nvidia/nvidia_decoder_factory.cpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp b/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp index bc8ca2cf7..147f060fe 100644 --- a/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp +++ b/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp @@ -52,12 +52,7 @@ bool IsNvdecRuntimeAvailable() { } // namespace -static int GetCudaDeviceCapabilityMajorVersion(CUcontext context) { - cuCtxSetCurrent(context); - - CUdevice device; - cuCtxGetDevice(&device); - +static int GetCudaDeviceCapabilityMajorVersion(CUdevice device) { int major; cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device); @@ -65,14 +60,14 @@ static int GetCudaDeviceCapabilityMajorVersion(CUcontext context) { return major; } -std::vector SupportedNvDecoderCodecs(CUcontext context) { +std::vector SupportedNvDecoderCodecs(CUdevice device) { std::vector supportedFormats; // HardwareGeneration Kepler is 3.x // https://docs.nvidia.com/deploy/cuda-compatibility/index.html#faq // Kepler support h264 profile Main, Highprofile up to Level4.1 // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide/index.html#video-decoder-capabilities__table_o3x_fms_3lb - if (GetCudaDeviceCapabilityMajorVersion(context) <= 3) { + if (GetCudaDeviceCapabilityMajorVersion(device) <= 3) { supportedFormats = { CreateH264Format(webrtc::H264Profile::kProfileHigh, webrtc::H264Level::kLevel4_1, "1"), @@ -117,13 +112,12 @@ NvidiaVideoDecoderFactory::NvidiaVideoDecoderFactory() return; } - cu_context_ = livekit_ffi::CudaContext::GetInstance(); - if (cu_context_->Initialize()) { - supported_formats_ = SupportedNvDecoderCodecs(cu_context_->GetContext()); - } else { - RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context."; - cu_context_ = nullptr; + CUdevice device; + if (cuDeviceGet(&device, 0) != CUDA_SUCCESS) { + RTC_LOG(LS_ERROR) << "Failed to get CUDA device."; + return; } + supported_formats_ = SupportedNvDecoderCodecs(device); RTC_LOG(LS_INFO) << "NvidiaVideoDecoderFactory created with " << supported_formats_.size() << " supported formats."; } From 956b4999320113a82b6c220c7409172c3aca676e Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 23 Sep 2026 18:34:46 -0700 Subject: [PATCH 2/4] Additional fixes --- webrtc-sys/src/nvidia/cuda_context.cpp | 5 +++++ webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp | 2 +- webrtc-sys/src/vaapi/vaapi_display_drm.cpp | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/webrtc-sys/src/nvidia/cuda_context.cpp b/webrtc-sys/src/nvidia/cuda_context.cpp index f46358d7c..ba64086b7 100644 --- a/webrtc-sys/src/nvidia/cuda_context.cpp +++ b/webrtc-sys/src/nvidia/cuda_context.cpp @@ -113,6 +113,7 @@ bool CudaContext::IsAvailable() { bool CudaContext::Initialize() { std::lock_guard lock(cudaMutex()); + std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); if (cu_context_ != nullptr) { ++ref_count_; RTC_LOG(LS_INFO) << "CUDA context already initialized; reusing existing " @@ -180,6 +181,9 @@ bool CudaContext::Initialize() { cu_context_ = context; ref_count_ = 1; RTC_LOG(LS_INFO) << "CUDA context initialized (refs=1)."; + std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); + auto duration_ms = std::chrono::duration_cast(end_time - start_time); + std::cout << "CUDA context initialization time: " << duration_ms.count() << " ms" << std::endl; return true; } @@ -237,6 +241,7 @@ void CudaContext::Shutdown() { "A later Initialize() will create a new context (refs=0)."; } else { RTC_LOG(LS_INFO) << "CUDA context destroyed successfully (refs=0)."; + std::cout << "CUDA context destroyed successfully (refs=0)." << std::endl; } } if (s_module_ptr) { diff --git a/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp b/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp index 147f060fe..740e180aa 100644 --- a/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp +++ b/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp @@ -148,7 +148,7 @@ bool NvidiaVideoDecoderFactory::IsSupported() { return false; } - std::cout << "Nvidia Decoder is supported." << std::endl; + RTC_LOG(LS_INFO) << "Nvidia Decoder is supported."; return true; } diff --git a/webrtc-sys/src/vaapi/vaapi_display_drm.cpp b/webrtc-sys/src/vaapi/vaapi_display_drm.cpp index be2259711..53cba2bf1 100644 --- a/webrtc-sys/src/vaapi/vaapi_display_drm.cpp +++ b/webrtc-sys/src/vaapi/vaapi_display_drm.cpp @@ -41,6 +41,7 @@ static bool check_h264_encoding_support(VADisplay va_display) { entrypoints = new VAEntrypoint[num_entrypoints * sizeof(*entrypoints)]; if (!entrypoints) { RTC_LOG(LS_ERROR) << "failed to allocate VA entrypoints"; + vaTerminate(va_display); return false; } @@ -75,10 +76,12 @@ static bool check_h264_encoding_support(VADisplay va_display) { << "Can't find VAEntrypointEncSlice or VAEntrypointEncSliceLP for " "H264 profiles"; delete[] entrypoints; + vaTerminate(va_display); return false; } delete[] entrypoints; + vaTerminate(va_display); return true; } From 14e61233a6d9732cd5b2f91c79769d730153fcd3 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 24 Sep 2026 10:16:52 -0700 Subject: [PATCH 3/4] Cleanup --- webrtc-sys/src/nvidia/cuda_context.cpp | 6 ------ webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/webrtc-sys/src/nvidia/cuda_context.cpp b/webrtc-sys/src/nvidia/cuda_context.cpp index ba64086b7..9355d8b11 100644 --- a/webrtc-sys/src/nvidia/cuda_context.cpp +++ b/webrtc-sys/src/nvidia/cuda_context.cpp @@ -9,7 +9,6 @@ #include #endif -#include #include #if defined(WIN32) @@ -113,7 +112,6 @@ bool CudaContext::IsAvailable() { bool CudaContext::Initialize() { std::lock_guard lock(cudaMutex()); - std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); if (cu_context_ != nullptr) { ++ref_count_; RTC_LOG(LS_INFO) << "CUDA context already initialized; reusing existing " @@ -181,9 +179,6 @@ bool CudaContext::Initialize() { cu_context_ = context; ref_count_ = 1; RTC_LOG(LS_INFO) << "CUDA context initialized (refs=1)."; - std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); - auto duration_ms = std::chrono::duration_cast(end_time - start_time); - std::cout << "CUDA context initialization time: " << duration_ms.count() << " ms" << std::endl; return true; } @@ -241,7 +236,6 @@ void CudaContext::Shutdown() { "A later Initialize() will create a new context (refs=0)."; } else { RTC_LOG(LS_INFO) << "CUDA context destroyed successfully (refs=0)."; - std::cout << "CUDA context destroyed successfully (refs=0)." << std::endl; } } if (s_module_ptr) { diff --git a/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp b/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp index d418bbcaf..2926a1c1e 100644 --- a/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp +++ b/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp @@ -1,7 +1,6 @@ #include "vaapi_encoder_factory.h" #include -#include #include #include "h264_encoder_impl.h" @@ -64,7 +63,7 @@ bool VAAPIVideoEncoderFactory::IsSupported() { vaapi_display.Close(); // If we can open the VAAPI display, we consider it supported. - std::cout << "VAAPI is supported." << std::endl; + RTC_LOG(LS_INFO) << "VAAPI is supported."; return true; } From 8a9a3b2de223a803c4c15257ee3e969a9ba8ed56 Mon Sep 17 00:00:00 2001 From: Alan George Date: Thu, 24 Sep 2026 16:31:03 -0700 Subject: [PATCH 4/4] Potential fix for server-deleted room memory leak --- .../src/room/participant/local_participant.rs | 12 ++++- livekit/tests/reconnection_test.rs | 48 +++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/livekit/src/room/participant/local_participant.rs b/livekit/src/room/participant/local_participant.rs index b4545fc1b..a2f533310 100644 --- a/livekit/src/room/participant/local_participant.rs +++ b/livekit/src/room/participant/local_participant.rs @@ -667,7 +667,11 @@ impl LocalParticipant { let track = publication.track().unwrap(); let sender = track.transceiver().unwrap().sender(); - self.inner.rtc_engine.remove_track(sender)?; + let remove_result = self.inner.rtc_engine.remove_track(sender); + + // The peer connection may already be closed after a server-initiated + // disconnect. Always release the local publication/track graph even + // when removing its sender is no longer possible. track.set_transceiver(None); if let Some(local_track_unpublished) = @@ -677,7 +681,11 @@ impl LocalParticipant { } publication.set_track(None); - self.inner.rtc_engine.publisher_negotiation_needed(); + if remove_result.is_ok() { + self.inner.rtc_engine.publisher_negotiation_needed(); + } + + remove_result?; Ok(publication) } else { diff --git a/livekit/tests/reconnection_test.rs b/livekit/tests/reconnection_test.rs index 2d9d7be1a..ebf360177 100644 --- a/livekit/tests/reconnection_test.rs +++ b/livekit/tests/reconnection_test.rs @@ -39,15 +39,23 @@ use { anyhow::{anyhow, bail, Result}, common::test_rooms, - libwebrtc::native::create_random_uuid, - livekit::{ConnectionState, Room, RoomEvent, RoomOptions, SimulateScenario}, + libwebrtc::{ + native::create_random_uuid, + prelude::{RtcVideoSource, VideoResolution}, + video_source::native::NativeVideoSource, + }, + livekit::{ + options::TrackPublishOptions, + track::{LocalTrack, LocalVideoTrack}, + ConnectionState, Room, RoomEvent, RoomOptions, SimulateScenario, + }, livekit_api::services::room::RoomClient, livekit_token::{AccessToken, VideoGrants}, std::{env, net::SocketAddr, time::Duration}, tokio::{ net::{TcpListener, TcpStream}, sync::{mpsc::UnboundedReceiver, watch}, - time::timeout, + time::{self, timeout}, }, }; @@ -396,6 +404,19 @@ async fn test_room_deleted_disconnects_without_reconnect() -> Result<()> { let (room, mut events) = Room::connect(&server_url, &token, RoomOptions::default()).await?; assert_eq!(room.connection_state(), ConnectionState::Connected); + let session_dropped = room.drop_probe(); + + let source = NativeVideoSource::new(VideoResolution { width: 16, height: 16 }, false); + let track = LocalVideoTrack::create_video_track( + "server-deleted-track", + RtcVideoSource::Native(source.clone()), + ); + let publication = room + .local_participant() + .publish_track(LocalTrack::Video(track), TrackPublishOptions::default()) + .await?; + assert!(publication.track().is_some()); + let http_url = server_url.replacen("ws", "http", 1); RoomClient::with_api_key(&http_url, &api_key, &api_secret).delete_room(&room_name).await?; @@ -413,5 +434,26 @@ async fn test_room_deleted_disconnects_without_reconnect() -> Result<()> { assert_eq!(reason, livekit::DisconnectReason::RoomDeleted); assert_eq!(room.connection_state(), ConnectionState::Disconnected); + + timeout(Duration::from_secs(5), async { + while publication.track().is_some() { + time::sleep(Duration::from_millis(10)).await; + } + }) + .await + .map_err(|_| anyhow!("server deletion did not detach the published local track"))?; + + drop(publication); + drop(source); + drop(events); + drop(room); + + timeout(Duration::from_secs(5), async { + while !session_dropped() { + time::sleep(Duration::from_millis(10)).await; + } + }) + .await + .map_err(|_| anyhow!("published track retained the room session after server deletion"))?; Ok(()) }