[fix] add capture delivery diagnostics

This commit is contained in:
dijunkun
2026-09-16 12:54:06 +08:00
parent f3e67d7d6e
commit d8ccdbaa38
4 changed files with 106 additions and 3 deletions
@@ -1,11 +1,12 @@
#include "features/devices/session_device_manager.h"
#include <display_stream_id.h>
#include <remote_action.h>
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <display_stream_id.h>
#include "platform.h"
#include "rd_log.h"
#include "runtime/gui_runtime.h"
@@ -59,6 +60,53 @@ bool SessionDeviceManager::ShouldSendCapturedFrame(
return true;
}
void SessionDeviceManager::RecordCaptureCadence(
std::chrono::steady_clock::time_point now, int fps,
bool from_secure_desktop) {
std::lock_guard<std::mutex> lock(capture_metrics_mutex_);
if (capture_metrics_started_ == std::chrono::steady_clock::time_point{})
capture_metrics_started_ = now;
const auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(
now - capture_metrics_started_)
.count();
if (elapsed_us >= 2000000) {
if (capture_forwarded_) {
const char* source = capture_secure_callbacks_ == 0 ? "normal_desktop"
: capture_secure_callbacks_ == capture_callbacks_
? "secure_desktop"
: "mixed";
LOG_INFO(
"Capture delivery: source={} normal_callbacks={} secure_callbacks={} "
"target_fps={} input_fps={:.2f} "
"forwarded_fps={:.2f} callbacks={} rate_dropped={} "
"forwarded={} send_errors={} max_callback_gap_us={} "
"send_avg_us={} send_max_us={} window_ms={}",
source, capture_callbacks_ - capture_secure_callbacks_,
capture_secure_callbacks_, fps,
capture_callbacks_ * 1000000.0 / elapsed_us,
capture_forwarded_ * 1000000.0 / elapsed_us, capture_callbacks_,
capture_rate_drops_, capture_forwarded_, capture_send_errors_,
capture_max_gap_us_, capture_send_us_ / capture_forwarded_,
capture_max_send_us_, elapsed_us / 1000);
}
capture_metrics_started_ = now;
capture_callbacks_ = capture_rate_drops_ = capture_forwarded_ = 0;
capture_secure_callbacks_ = 0;
capture_send_errors_ = 0;
capture_max_gap_us_ = capture_send_us_ = capture_max_send_us_ = 0;
}
if (last_capture_callback_ != std::chrono::steady_clock::time_point{}) {
capture_max_gap_us_ =
std::max<int64_t>(capture_max_gap_us_,
std::chrono::duration_cast<std::chrono::microseconds>(
now - last_capture_callback_)
.count());
}
last_capture_callback_ = std::max(last_capture_callback_, now);
++capture_callbacks_;
if (from_secure_desktop) ++capture_secure_callbacks_;
}
void SessionDeviceManager::Initialize() {
InitializeAudioOutput();
screen_capturer_factory_ = new ScreenCapturerFactory();
@@ -88,6 +136,11 @@ int SessionDeviceManager::InitializeScreenCapturer() {
last_frame_time_ = {};
next_frame_deadline_ = {};
capture_metrics_started_ = last_capture_callback_ = {};
capture_callbacks_ = capture_rate_drops_ = capture_forwarded_ = 0;
capture_secure_callbacks_ = 0;
capture_send_errors_ = 0;
capture_max_gap_us_ = capture_send_us_ = capture_max_send_us_ = 0;
const int fps = owner_.config_center_->GetVideoFrameRate() ==
ConfigCenter::VIDEO_FRAME_RATE::FPS_30
? 30
@@ -103,7 +156,15 @@ int SessionDeviceManager::InitializeScreenCapturer() {
const char *display_name,
const MiniRtcNativeVideoFrame *native_frame) {
const auto now_time = std::chrono::steady_clock::now();
bool from_secure_desktop = false;
#ifdef _WIN32
from_secure_desktop =
ScreenCapturerWin::CurrentFrameIsFromSecureDesktop();
#endif
RecordCaptureCadence(now_time, fps, from_secure_desktop);
if (!ShouldSendCapturedFrame(now_time, fps)) {
std::lock_guard<std::mutex> lock(capture_metrics_mutex_);
++capture_rate_drops_;
return;
}
@@ -170,9 +231,24 @@ int SessionDeviceManager::InitializeScreenCapturer() {
frame.height = height;
frame.captured_timestamp = GetSystemTimeMicros(owner_.peer_);
frame.native_frame = native_frame;
const auto send_started = std::chrono::steady_clock::now();
uint64_t send_errors = 0;
for (const std::string &remote_id : connected_remote_ids) {
SendVideoFrameToPeer(owner_.peer_, &frame, stream_id.c_str(),
remote_id.data(), remote_id.size());
if (SendVideoFrameToPeer(owner_.peer_, &frame, stream_id.c_str(),
remote_id.data(), remote_id.size()) != 0)
++send_errors;
}
const auto send_us =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - send_started)
.count();
{
std::lock_guard<std::mutex> lock(capture_metrics_mutex_);
++capture_forwarded_;
capture_send_errors_ += send_errors;
capture_send_us_ += send_us;
capture_max_send_us_ =
std::max<int64_t>(capture_max_send_us_, send_us);
}
last_video_frame_stream_id_ = stream_id;
last_frame_time_ = now_time;
@@ -84,6 +84,8 @@ private:
void ClearCapturedKeyboardInput();
bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now,
int fps);
void RecordCaptureCadence(std::chrono::steady_clock::time_point now, int fps,
bool from_secure_desktop);
GuiRuntime &owner_;
SDL_AudioStream *output_stream_ = nullptr;
@@ -99,6 +101,14 @@ private:
std::mutex captured_keyboard_inputs_mutex_;
std::chrono::steady_clock::time_point last_frame_time_{};
std::chrono::steady_clock::time_point next_frame_deadline_{};
std::mutex capture_metrics_mutex_;
std::chrono::steady_clock::time_point capture_metrics_started_{};
std::chrono::steady_clock::time_point last_capture_callback_{};
uint64_t capture_callbacks_ = 0, capture_rate_drops_ = 0;
uint64_t capture_secure_callbacks_ = 0;
uint64_t capture_forwarded_ = 0, capture_send_errors_ = 0;
int64_t capture_max_gap_us_ = 0;
int64_t capture_send_us_ = 0, capture_max_send_us_ = 0;
std::string last_video_frame_stream_id_;
bool invalid_video_stream_id_logged_ = false;
};
@@ -33,6 +33,8 @@ namespace {
using Json = nlohmann::json;
thread_local bool current_frame_from_secure_desktop = false;
constexpr DWORD kSecureDesktopStatusIntervalMs = 250;
constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 500;
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
@@ -460,6 +462,10 @@ int ScreenCapturerWin::Destroy() {
return 0;
}
bool ScreenCapturerWin::CurrentFrameIsFromSecureDesktop() {
return current_frame_from_secure_desktop;
}
void ScreenCapturerWin::EmitCapturedFrame(
unsigned char* data, int size, int width, int height,
const char* stream_id, const MiniRtcNativeVideoFrame* native_frame,
@@ -473,6 +479,14 @@ void ScreenCapturerWin::EmitCapturedFrame(
return;
}
// Normal and helper callbacks can originate on different threads during
// desktop transitions. Keep diagnostics tied to this callback's source.
struct RestoreFrameSource {
bool previous;
~RestoreFrameSource() { current_frame_from_secure_desktop = previous; }
} restore_source{
std::exchange(current_frame_from_secure_desktop, from_secure_desktop)};
if (native_frame) {
if (!native_output_logged_.exchange(true, std::memory_order_relaxed)) {
LOG_INFO("Windows capturer native frame output enabled (type={})",
@@ -48,6 +48,9 @@ class ScreenCapturerWin : public ScreenCapturer {
void SetPrivacyController(PrivacyController* privacy) override { privacy_ = privacy; }
// Set before Init(), while capture is stopped.
void SetCaptureMethod(ScreenCaptureMethod method) { capture_method_ = method; }
// Query synchronously inside the frame callback, on the delivering thread.
// This describes the actual frame, independently of service status polling.
static bool CurrentFrameIsFromSecureDesktop();
private:
std::unique_ptr<ScreenCapturer> impl_;