[refactor] modularize Slint video rendering backends

This commit is contained in:
dijunkun
2026-08-25 11:45:38 +08:00
parent 21eec76008
commit 8ea4668305
18 changed files with 799 additions and 771 deletions
+2 -19
View File
@@ -11,12 +11,7 @@
#include "display_stream_id.h"
#include "localization.h"
#include "platform.h"
#if defined(_WIN32) || defined(__linux__)
#include "platform/opengl_video_renderer.h"
#endif
#ifdef __APPLE__
#include "platform/metal_video_renderer.h"
#endif
#include "platform/video_renderer.h"
#include "rd_log.h"
#include "runtime/gui_runtime.h"
@@ -229,21 +224,14 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
frame_snapshot = props->front_frame_;
video_width = props->video_width_;
video_height = props->video_height_;
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if ((!frame_snapshot || frame_snapshot->empty()) &&
props->thumbnail_frame_ && !props->thumbnail_frame_->empty()) {
frame_snapshot = props->thumbnail_frame_;
video_width = props->thumbnail_width_;
video_height = props->thumbnail_height_;
}
#endif
}
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#if defined(__APPLE__)
auto* native_renderer = mac_metal_video_renderer_.get();
#else
auto* native_renderer = opengl_video_renderer_.get();
#endif
auto* native_renderer = video_renderer_.get();
if ((!frame_snapshot || frame_snapshot->empty()) && native_renderer) {
auto native_snapshot = std::make_shared<std::vector<unsigned char>>();
if (native_renderer->CopyLatestNv12(props->remote_id_,
@@ -252,7 +240,6 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
frame_snapshot = std::move(native_snapshot);
}
}
#endif
if (frame_snapshot && !frame_snapshot->empty() && video_width > 0 &&
video_height > 0) {
@@ -276,12 +263,10 @@ void GuiRuntime::CloseRemoteSession(std::shared_ptr<RemoteSession> props) {
}
}
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (native_renderer) {
native_renderer->DiscardStream(props->remote_id_);
video_frame_dirty_.store(true, std::memory_order_release);
}
#endif
if (props->peer_) {
LOG_INFO("[{}] Leave connection [{}]", props->local_id_, props->remote_id_);
@@ -343,12 +328,10 @@ void GuiRuntime::ResetRemoteSessionResources(
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
props->front_frame_.reset();
props->back_frame_.reset();
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
props->thumbnail_frame_.reset();
props->thumbnail_width_ = 0;
props->thumbnail_height_ = 0;
props->background_snapshot_time_ = {};
#endif
props->video_width_ = 0;
props->video_height_ = 0;
props->video_size_ = 0;
+1 -6
View File
@@ -12,12 +12,7 @@
#include "display_stream_id.h"
#include "localization.h"
#if defined(_WIN32) || defined(__linux__)
#include "platform/opengl_video_renderer.h"
#endif
#ifdef __APPLE__
#include "platform/metal_video_renderer.h"
#endif
#include "platform/video_renderer.h"
#include "rd_log.h"
namespace crossdesk {
+2 -12
View File
@@ -15,12 +15,7 @@
namespace crossdesk {
#if defined(_WIN32) || defined(__linux__)
class OpenGlVideoRenderer;
#endif
#ifdef __APPLE__
class MacMetalVideoRenderer;
#endif
class VideoRenderer;
// Shared GUI runtime. It owns subsystem controllers and cross-cutting session
// state, but no window lifecycle, ImGui view, or transport callback methods.
@@ -90,12 +85,7 @@ class GuiRuntime : protected gui_detail::GuiState {
KeyboardController keyboard_;
PeerEventHandler peer_events_;
std::atomic<bool> video_frame_dirty_{false};
#if defined(_WIN32) || defined(__linux__)
std::unique_ptr<OpenGlVideoRenderer> opengl_video_renderer_;
#endif
#ifdef __APPLE__
std::unique_ptr<MacMetalVideoRenderer> mac_metal_video_renderer_;
#endif
std::unique_ptr<VideoRenderer> video_renderer_;
private:
friend class ClipboardController;
+10 -30
View File
@@ -16,12 +16,7 @@
#include "file_transfer.h"
#include "localization.h"
#include "platform.h"
#if defined(_WIN32) || defined(__linux__)
#include "platform/opengl_video_renderer.h"
#endif
#ifdef __APPLE__
#include "platform/metal_video_renderer.h"
#endif
#include "platform/video_renderer.h"
#include "rd_log.h"
#include "runtime/gui_runtime.h"
#include "runtime/remote_action_codec.h"
@@ -319,61 +314,46 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
props->enable_mouse_control_ = false;
runtime->ResetRemoteServiceStatus(*props);
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
std::shared_ptr<std::vector<unsigned char>> native_snapshot;
int native_snapshot_width = 0;
int native_snapshot_height = 0;
bool needs_native_snapshot = false;
{
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
needs_native_snapshot = !props->thumbnail_frame_ ||
props->thumbnail_frame_->empty();
needs_native_snapshot =
!props->thumbnail_frame_ || props->thumbnail_frame_->empty();
}
if (needs_native_snapshot) {
#if defined(__APPLE__)
auto* native_renderer = runtime->mac_metal_video_renderer_.get();
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
#endif
auto* native_renderer = runtime->video_renderer_.get();
auto snapshot = std::make_shared<std::vector<unsigned char>>();
if (native_renderer && native_renderer->CopyLatestNv12(
remote_id, snapshot.get(),
&native_snapshot_width,
&native_snapshot_height)) {
if (native_renderer &&
native_renderer->CopyLatestNv12(remote_id, snapshot.get(),
&native_snapshot_width,
&native_snapshot_height)) {
native_snapshot = std::move(snapshot);
}
}
#endif
{
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
props->front_frame_.reset();
props->back_frame_.reset();
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (native_snapshot &&
(!props->thumbnail_frame_ || props->thumbnail_frame_->empty())) {
props->thumbnail_frame_ = std::move(native_snapshot);
props->thumbnail_width_ = native_snapshot_width;
props->thumbnail_height_ = native_snapshot_height;
}
#endif
props->video_width_ = 0;
props->video_height_ = 0;
props->video_size_ = 0;
props->render_rect_dirty_ = true;
props->stream_cleanup_pending_ = true;
}
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#if defined(__APPLE__)
auto* native_renderer = runtime->mac_metal_video_renderer_.get();
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
#endif
auto* native_renderer = runtime->video_renderer_.get();
if (native_renderer) {
native_renderer->DiscardStream(remote_id);
runtime->video_frame_dirty_.store(true,
std::memory_order_release);
runtime->video_frame_dirty_.store(true, std::memory_order_release);
}
#endif
runtime->focus_on_stream_window_ = false;
+21 -51
View File
@@ -1,36 +1,26 @@
#include "runtime/peer_event_handler.h"
#include <chrono>
#include <cstring>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "platform/video_renderer.h"
#include "runtime/gui_runtime.h"
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
#include <chrono>
#if defined(__APPLE__)
#include "platform/metal_video_renderer.h"
#else
#include "platform/opengl_video_renderer.h"
#endif
#endif
#include "runtime/peer_event_handler.h"
namespace crossdesk {
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
namespace {
constexpr auto kBackgroundSnapshotInterval = std::chrono::seconds(1);
} // namespace
#endif
void PeerEventHandler::OnReceiveVideoBuffer(
const XVideoFrame *video_frame, const char *user_id, size_t user_id_size,
const char *src_id, size_t src_id_size, void *user_data) {
auto *handler = static_cast<PeerEventHandler *>(user_data);
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
const XVideoFrame* video_frame, const char* user_id, size_t user_id_size,
const char* src_id, size_t src_id_size, void* user_data) {
auto* handler = static_cast<PeerEventHandler*>(user_data);
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
if (!runtime) {
return;
}
@@ -41,32 +31,20 @@ void PeerEventHandler::OnReceiveVideoBuffer(
runtime->remote_sessions_.end()) {
return;
}
GuiRuntime::RemoteSession *props =
GuiRuntime::RemoteSession* props =
runtime->remote_sessions_.find(remote_id)->second.get();
if (props->connection_established_) {
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
bool background_snapshot_only = false;
#if defined(__APPLE__)
auto* native_renderer = runtime->mac_metal_video_renderer_.get();
using NativeVideoRenderer = MacMetalVideoRenderer;
#else
auto* native_renderer = runtime->opengl_video_renderer_.get();
using NativeVideoRenderer = OpenGlVideoRenderer;
#endif
if (native_renderer && native_renderer->IsReady()
#if defined(__APPLE__)
&& native_renderer->IsAttached()
#endif
) {
auto* native_renderer = runtime->video_renderer_.get();
if (native_renderer && native_renderer->IsActive()) {
const auto submit_result = native_renderer->SubmitNv12(
remote_id, reinterpret_cast<const uint8_t*>(video_frame->data),
video_frame->size, video_frame->width, video_frame->height);
if (submit_result == NativeVideoRenderer::SubmitResult::submitted) {
if (submit_result == VideoRenderer::SubmitResult::submitted) {
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
const bool size_changed =
(props->video_width_ != video_frame->width) ||
(props->video_height_ != video_frame->height);
const bool size_changed = (props->video_width_ != video_frame->width) ||
(props->video_height_ != video_frame->height);
if (size_changed) {
props->render_rect_dirty_ = true;
}
@@ -86,22 +64,19 @@ void PeerEventHandler::OnReceiveVideoBuffer(
runtime->video_frame_dirty_.store(true, std::memory_order_release);
return;
}
if (submit_result == NativeVideoRenderer::SubmitResult::dropped ||
submit_result == NativeVideoRenderer::SubmitResult::failed) {
if (submit_result == VideoRenderer::SubmitResult::dropped ||
submit_result == VideoRenderer::SubmitResult::failed) {
// Keep presenting the last native frame. A later decoded frame can
// reuse the renderer without changing ownership mid-window.
props->streaming_ = true;
return;
}
if (submit_result ==
NativeVideoRenderer::SubmitResult::not_selected) {
if (submit_result == VideoRenderer::SubmitResult::not_selected) {
background_snapshot_only = true;
}
}
#endif
{
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
const auto now = std::chrono::steady_clock::now();
if (background_snapshot_only && props->thumbnail_frame_ &&
!props->thumbnail_frame_->empty() &&
@@ -112,7 +87,6 @@ void PeerEventHandler::OnReceiveVideoBuffer(
props->streaming_ = true;
return;
}
#endif
// Allocate a third buffer only while the UI still owns the old snapshot.
if (!props->back_frame_ || props->back_frame_.use_count() != 1) {
@@ -137,32 +111,28 @@ void PeerEventHandler::OnReceiveVideoBuffer(
props->video_size_ = video_frame->size;
props->front_frame_.swap(props->back_frame_);
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
props->thumbnail_frame_ = props->front_frame_;
props->thumbnail_width_ = video_frame->width;
props->thumbnail_height_ = video_frame->height;
if (background_snapshot_only) {
props->background_snapshot_time_ = now;
}
#endif
++props->video_frame_sequence_;
}
props->streaming_ = true;
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
if (background_snapshot_only) {
return;
}
#endif
runtime->video_frame_dirty_.store(true, std::memory_order_release);
}
}
void PeerEventHandler::OnReceiveAudioBuffer(
const char *data, size_t size, const char *user_id, size_t user_id_size,
const char *src_id, size_t src_id_size, void *user_data) {
auto *handler = static_cast<PeerEventHandler *>(user_data);
GuiRuntime *runtime = handler ? &handler->owner_ : nullptr;
const char* data, size_t size, const char* user_id, size_t user_id_size,
const char* src_id, size_t src_id_size, void* user_data) {
auto* handler = static_cast<PeerEventHandler*>(user_data);
GuiRuntime* runtime = handler ? &handler->owner_ : nullptr;
if (!runtime) {
return;
}
@@ -172,4 +142,4 @@ void PeerEventHandler::OnReceiveAudioBuffer(
runtime->devices_.PushAudio(data, size);
}
} // namespace crossdesk
} // namespace crossdesk
-2
View File
@@ -110,14 +110,12 @@ struct RemoteSession {
std::mutex video_frame_mutex_;
std::shared_ptr<std::vector<unsigned char>> front_frame_;
std::shared_ptr<std::vector<unsigned char>> back_frame_;
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
// Retains the most recent background CPU frame across disconnect cleanup
// so closing an unselected tab can still update its recent-item thumbnail.
std::shared_ptr<std::vector<unsigned char>> thumbnail_frame_;
int thumbnail_width_ = 0;
int thumbnail_height_ = 0;
std::chrono::steady_clock::time_point background_snapshot_time_;
#endif
bool render_rect_dirty_ = false;
bool stream_cleanup_pending_ = false;
float mouse_pos_x_ = 0;