From 485e3473370a3d396a23b6f4f7eadfbe40ee72c2 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Mon, 21 Sep 2026 00:51:50 +0800 Subject: [PATCH] [fix] render CPU NV12 native frames rejected by the native renderer --- .../src/gui/runtime/peer_media_callbacks.cpp | 25 ++++++++++++++++--- apps/desktop/src/gui/runtime/remote_session.h | 3 +++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/gui/runtime/peer_media_callbacks.cpp b/apps/desktop/src/gui/runtime/peer_media_callbacks.cpp index 8ae5f734..18426a57 100644 --- a/apps/desktop/src/gui/runtime/peer_media_callbacks.cpp +++ b/apps/desktop/src/gui/runtime/peer_media_callbacks.cpp @@ -50,9 +50,28 @@ void PeerEventHandler::OnReceiveVideoBuffer( if (native_renderer && native_renderer->IsActive()) { VideoRenderer::SubmitResult submit_result = VideoRenderer::SubmitResult::failed; - if (GetNativeVideoFrame(video_frame)) { - submit_result = native_renderer->SubmitNativeFrame( - remote_id, *video_frame->native_frame); + if (const auto* native = GetNativeVideoFrame(video_frame)) { + submit_result = native_renderer->SubmitNativeFrame(remote_id, *native); + // A CPU NV12 native frame is valid MiniRTC input, but the macOS Metal + // fast path only accepts CVPixelBuffer. Materialize it when that path + // rejects a software decoder's descriptor. This also supports peers + // using CPU decoding when their platform hardware decoder is unavailable. + if (submit_result == VideoRenderer::SubmitResult::failed && + native->type == MiniRtcNativeVideoFrameCpuNv12) { + const size_t frame_size = + static_cast(native->width) * native->height * 3U / 2U; + // One decode callback thread per session; keep the buffer across + // frames instead of a full-frame allocation per rejected frame. + auto& fallback = props->native_cpu_fallback_; + if (fallback.size() != frame_size) fallback.resize(frame_size); + if (native->copy_to_nv12(native->owner, fallback.data(), + fallback.size()) == 0) { + submit_result = native_renderer->SubmitNv12( + remote_id, fallback.data(), fallback.size(), + static_cast(native->width), + static_cast(native->height)); + } + } } else { submit_result = native_renderer->SubmitNv12( diff --git a/apps/desktop/src/gui/runtime/remote_session.h b/apps/desktop/src/gui/runtime/remote_session.h index 97a452e3..a30edde5 100644 --- a/apps/desktop/src/gui/runtime/remote_session.h +++ b/apps/desktop/src/gui/runtime/remote_session.h @@ -94,6 +94,9 @@ struct RemoteSession { bool p2p_mode_ = true; bool remember_password_ = false; char remote_password_[7] = ""; + // Reused across frames by the decode callback thread when a CPU NV12 + // native frame has to be materialized for a renderer that rejects it. + std::vector native_cpu_fallback_; // Written by the decode callback thread and consumed by the UI thread. std::mutex video_frame_mutex_;