fix: 断联视频堆积问题与控制命令失效问题

This commit is contained in:
2026-04-11 03:55:19 +08:00
parent 6f727dbe57
commit 84e0cc54d2
8 changed files with 381 additions and 9 deletions

View File

@@ -28,6 +28,11 @@
#define VIDEO_DEFAULT_CAMERA_DEVICE "/dev/video0"
#define VIDEO_DEFAULT_PEER_ID "peer-b-video"
#define VIDEO_DEFAULT_TARGET_PEER "peer-a-video"
#define VIDEO_SOFT_BACKPRESSURE_SEGMENTS_DEFAULT 64
#define VIDEO_HARD_BACKPRESSURE_SEGMENTS_DEFAULT 192
#define VIDEO_HARD_BACKPRESSURE_HOLD_MS_DEFAULT 1000
#define VIDEO_SOFT_BACKPRESSURE_WINDOW_PRESSURE_PCT 90.0
#define VIDEO_HARD_BACKPRESSURE_WINDOW_PRESSURE_PCT 98.0
typedef struct video_buffer {
void *start;
@@ -137,6 +142,20 @@ static const char *env_first_nonempty(const char *first, const char *second, con
return fallback;
}
static int env_int_or_default(const char *name, int fallback) {
const char *value = getenv(name);
int parsed;
if (value == NULL || value[0] == '\0') {
return fallback;
}
parsed = atoi(value);
if (parsed <= 0) {
return fallback;
}
return parsed;
}
static void video_pipeline_set_error(video_pipeline_stats_t *stats, const char *message) {
if (stats == NULL) {
return;
@@ -179,6 +198,9 @@ void video_pipeline_config_init(video_pipeline_config_t *config) {
config->output_height = VIDEO_OUTPUT_HEIGHT_DEFAULT;
config->max_frames = 0;
config->enable_timing_logs = 0;
config->soft_backpressure_segments = VIDEO_SOFT_BACKPRESSURE_SEGMENTS_DEFAULT;
config->hard_backpressure_segments = VIDEO_HARD_BACKPRESSURE_SEGMENTS_DEFAULT;
config->hard_backpressure_hold_ms = VIDEO_HARD_BACKPRESSURE_HOLD_MS_DEFAULT;
}
void video_pipeline_config_load_env(video_pipeline_config_t *config) {
@@ -196,6 +218,9 @@ void video_pipeline_config_load_env(video_pipeline_config_t *config) {
config->max_frames = atoi(getenv("OMNI_VIDEO_MAX_FRAMES"));
}
config->enable_timing_logs = env_flag_or_default("OMNI_VIDEO_DEBUG_TIMING", config->enable_timing_logs);
config->soft_backpressure_segments = env_int_or_default("OMNI_VIDEO_SOFT_BACKPRESSURE_SEGMENTS", config->soft_backpressure_segments);
config->hard_backpressure_segments = env_int_or_default("OMNI_VIDEO_HARD_BACKPRESSURE_SEGMENTS", config->hard_backpressure_segments);
config->hard_backpressure_hold_ms = env_int_or_default("OMNI_VIDEO_HARD_BACKPRESSURE_HOLD_MS", config->hard_backpressure_hold_ms);
}
int video_pipeline_stats_init(video_pipeline_stats_t *stats) {
@@ -229,9 +254,13 @@ void video_pipeline_stats_snapshot(video_pipeline_stats_t *stats, video_pipeline
out_stats->frames_sent = stats->frames_sent;
out_stats->bytes_sent = stats->bytes_sent;
out_stats->send_errors = stats->send_errors;
out_stats->backpressure_drops = stats->backpressure_drops;
out_stats->backlog_resets = stats->backlog_resets;
out_stats->last_frame_bytes = stats->last_frame_bytes;
out_stats->last_backlog_segments = stats->last_backlog_segments;
out_stats->connected = stats->connected;
snprintf(out_stats->last_error, sizeof(out_stats->last_error), "%s", stats->last_error);
snprintf(out_stats->last_backlog_reason, sizeof(out_stats->last_backlog_reason), "%s", stats->last_backlog_reason);
out_stats->transport = stats->transport;
pthread_mutex_unlock(&stats->mutex);
}
@@ -632,6 +661,56 @@ static void video_sender_close(video_sender_t *sender) {
sender->send_buffer_cap = 0;
}
static uint32_t video_sender_backlog_segments(const kcp_runtime_stats_t *stats) {
if (stats == NULL) {
return 0;
}
return stats->snd_queue + stats->snd_buffer;
}
static int video_sender_soft_backpressure_active(const video_pipeline_config_t *config, const kcp_runtime_stats_t *transport) {
if (config == NULL || transport == NULL) {
return 0;
}
return video_sender_backlog_segments(transport) >= (uint32_t) config->soft_backpressure_segments
|| transport->window_pressure_pct >= VIDEO_SOFT_BACKPRESSURE_WINDOW_PRESSURE_PCT;
}
static int video_sender_hard_backpressure_active(const video_pipeline_config_t *config, const kcp_runtime_stats_t *transport) {
if (config == NULL || transport == NULL) {
return 0;
}
return video_sender_backlog_segments(transport) >= (uint32_t) config->hard_backpressure_segments
|| transport->window_pressure_pct >= VIDEO_HARD_BACKPRESSURE_WINDOW_PRESSURE_PCT;
}
static void video_pipeline_note_backpressure(
video_pipeline_stats_t *stats,
const char *reason,
const kcp_runtime_stats_t *transport,
int increment_drop,
int increment_reset
) {
if (stats == NULL) {
return;
}
pthread_mutex_lock(&stats->mutex);
if (increment_drop) {
stats->backpressure_drops += 1;
}
if (increment_reset) {
stats->backlog_resets += 1;
}
if (transport != NULL) {
stats->last_backlog_segments = video_sender_backlog_segments(transport);
stats->transport = *transport;
} else {
stats->last_backlog_segments = 0;
}
snprintf(stats->last_backlog_reason, sizeof(stats->last_backlog_reason), "%s", reason == NULL ? "" : reason);
pthread_mutex_unlock(&stats->mutex);
}
static void video_pipeline_cleanup_buffers(video_buffer_t *buffers, int num_buffers) {
int i;
if (buffers == NULL) {
@@ -660,6 +739,8 @@ int video_pipeline_run(const video_pipeline_config_t *config, video_pipeline_sta
int sws_src_width = 0;
int sws_src_height = 0;
int sws_src_format = -1;
uint32_t hard_backpressure_since_ms = 0;
uint32_t last_soft_drop_log_ms = 0;
memset(&sender, 0, sizeof(sender));
if (stats == NULL) {
@@ -743,6 +824,7 @@ int video_pipeline_run(const video_pipeline_config_t *config, video_pipeline_sta
AVFrame *decoded_frame = NULL;
AVFrame *scaled_frame = NULL;
AVPacket *encoded_pkt = NULL;
kcp_runtime_stats_t transport_stats;
int select_rc;
double total_start_ms = 0.0;
double capture_start_ms = 0.0;
@@ -757,6 +839,8 @@ int video_pipeline_run(const video_pipeline_config_t *config, video_pipeline_sta
double send_end_ms = 0.0;
int frame_number = frame_index + 1;
memset(&transport_stats, 0, sizeof(transport_stats));
if (config->max_frames > 0 && frame_index >= config->max_frames) {
break;
}
@@ -840,6 +924,84 @@ int video_pipeline_run(const video_pipeline_config_t *config, video_pipeline_sta
send_start_ms = encode_end_ms;
}
kcp_client_runtime_stats_snapshot(sender.client, &transport_stats);
if (video_sender_hard_backpressure_active(config, &transport_stats)) {
uint32_t now_ms = omni_now_millis32();
if (hard_backpressure_since_ms == 0) {
hard_backpressure_since_ms = now_ms;
}
if (now_ms - hard_backpressure_since_ms >= (uint32_t) config->hard_backpressure_hold_ms) {
char reason[128];
uint32_t backlog_segments = video_sender_backlog_segments(&transport_stats);
snprintf(
reason,
sizeof(reason),
"hard_reset backlog=%u snd_queue=%u snd_buffer=%u window_pressure=%.1f%% hold_ms=%d",
backlog_segments,
transport_stats.snd_queue,
transport_stats.snd_buffer,
transport_stats.window_pressure_pct,
config->hard_backpressure_hold_ms
);
video_pipeline_note_backpressure(stats, reason, &transport_stats, 0, 1);
video_pipeline_set_error(stats, reason);
fprintf(
stderr,
"[video_pipeline] backlog hard reset: backlog=%u snd_queue=%u snd_buffer=%u window_pressure=%.1f%% hold_ms=%d\n",
backlog_segments,
transport_stats.snd_queue,
transport_stats.snd_buffer,
transport_stats.window_pressure_pct,
config->hard_backpressure_hold_ms
);
av_frame_free(&decoded_frame);
av_frame_free(&scaled_frame);
av_packet_free(&encoded_pkt);
(void) ioctl(fd, VIDIOC_QBUF, &buf);
rc = VIDEO_PIPELINE_RUN_RETRY_IMMEDIATE;
goto cleanup;
}
} else {
hard_backpressure_since_ms = 0;
}
if (video_sender_soft_backpressure_active(config, &transport_stats)) {
uint32_t now_ms = omni_now_millis32();
uint32_t backlog_segments = video_sender_backlog_segments(&transport_stats);
char reason[128];
snprintf(
reason,
sizeof(reason),
"soft_drop backlog=%u snd_queue=%u snd_buffer=%u window_pressure=%.1f%% threshold=%d",
backlog_segments,
transport_stats.snd_queue,
transport_stats.snd_buffer,
transport_stats.window_pressure_pct,
config->soft_backpressure_segments
);
video_pipeline_note_backpressure(stats, reason, &transport_stats, 1, 0);
if (now_ms - last_soft_drop_log_ms >= 1000U) {
fprintf(
stderr,
"[video_pipeline] soft drop: backlog=%u snd_queue=%u snd_buffer=%u window_pressure=%.1f%% threshold=%d\n",
backlog_segments,
transport_stats.snd_queue,
transport_stats.snd_buffer,
transport_stats.window_pressure_pct,
config->soft_backpressure_segments
);
last_soft_drop_log_ms = now_ms;
}
av_frame_free(&decoded_frame);
av_frame_free(&scaled_frame);
av_packet_free(&encoded_pkt);
(void) ioctl(fd, VIDIOC_QBUF, &buf);
continue;
}
if (video_sender_send_packet(&sender, encoded_pkt, (uint64_t) get_realtime_ms()) != 0) {
pthread_mutex_lock(&stats->mutex);
stats->send_errors += 1;