fix: 断联视频堆积问题与控制命令失效问题
This commit is contained in:
@@ -19,6 +19,7 @@ struct kcp_client {
|
||||
pthread_mutex_t state_mu;
|
||||
uint64_t next_message_id;
|
||||
int registered;
|
||||
uint32_t last_server_activity_ms;
|
||||
char last_server_error[256];
|
||||
};
|
||||
|
||||
@@ -42,6 +43,15 @@ static void kcp_client_set_registered(kcp_client_t *client, int registered) {
|
||||
pthread_mutex_unlock(&client->state_mu);
|
||||
}
|
||||
|
||||
static void kcp_client_touch_server_activity(kcp_client_t *client) {
|
||||
if (client == NULL) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&client->state_mu);
|
||||
client->last_server_activity_ms = omni_now_millis32();
|
||||
pthread_mutex_unlock(&client->state_mu);
|
||||
}
|
||||
|
||||
static void kcp_client_set_last_server_error(kcp_client_t *client, const char *message) {
|
||||
if (client == NULL) {
|
||||
return;
|
||||
@@ -55,6 +65,16 @@ static void kcp_client_clear_last_server_error(kcp_client_t *client) {
|
||||
kcp_client_set_last_server_error(client, "");
|
||||
}
|
||||
|
||||
static int kcp_client_server_error_invalidates_registration(const char *message) {
|
||||
if (message == NULL || message[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
return strstr(message, "not registered") != NULL
|
||||
|| strstr(message, "first message must be register") != NULL
|
||||
|| strstr(message, "peer replaced") != NULL
|
||||
|| strstr(message, "timed out waiting for server_register_ok") != NULL;
|
||||
}
|
||||
|
||||
static int kcp_client_is_registered(kcp_client_t *client) {
|
||||
int registered;
|
||||
|
||||
@@ -158,6 +178,7 @@ static int kcp_client_handle_reserved_server_message(kcp_client_t *client, const
|
||||
if (msg->type != MSG_TYPE_TEXT || strcmp(msg->from, SERVER_PEER_ID) != 0) {
|
||||
return 0;
|
||||
}
|
||||
kcp_client_touch_server_activity(client);
|
||||
if (kcp_client_text_body_equals(msg, KCP_CLIENT_CTRL_REGISTER_OK)) {
|
||||
kcp_client_set_registered(client, 1);
|
||||
kcp_client_clear_last_server_error(client);
|
||||
@@ -239,6 +260,7 @@ static int kcp_client_wait_for_register_ok(kcp_client_t *client) {
|
||||
char error_text[256];
|
||||
|
||||
kcp_client_copy_server_error_body(&msg, error_text, sizeof(error_text));
|
||||
kcp_client_touch_server_activity(client);
|
||||
kcp_client_set_registered(client, 0);
|
||||
kcp_client_set_last_server_error(client, error_text);
|
||||
protocol_message_clear(&msg);
|
||||
@@ -290,6 +312,9 @@ static int kcp_client_receive_business_timed(kcp_client_t *client, message_t *ou
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (strcmp(out_msg->from, SERVER_PEER_ID) == 0) {
|
||||
kcp_client_touch_server_activity(client);
|
||||
}
|
||||
reserved_rc = kcp_client_handle_reserved_server_message(client, out_msg);
|
||||
if (reserved_rc < 0) {
|
||||
protocol_message_clear(out_msg);
|
||||
@@ -304,6 +329,9 @@ static int kcp_client_receive_business_timed(kcp_client_t *client, message_t *ou
|
||||
|
||||
kcp_client_copy_server_error_body(out_msg, error_text, sizeof(error_text));
|
||||
kcp_client_set_last_server_error(client, error_text);
|
||||
if (kcp_client_server_error_invalidates_registration(error_text)) {
|
||||
kcp_client_set_registered(client, 0);
|
||||
}
|
||||
}
|
||||
latencylog_log_message_event(client->logger, OMNI_NODE_ROLE_PEER, client->id, EVENT_B_APP_RECV, out_msg);
|
||||
return 0;
|
||||
@@ -399,6 +427,7 @@ kcp_client_t *kcp_client_dial_with_options(const char *server_addr, const char *
|
||||
snprintf(client->id, sizeof(client->id), "%s", peer_id);
|
||||
snprintf(client->server_addr, sizeof(client->server_addr), "%s", server_addr == NULL ? "" : server_addr);
|
||||
pthread_mutex_init(&client->state_mu, NULL);
|
||||
client->last_server_activity_ms = omni_now_millis32();
|
||||
client->logger = logger;
|
||||
client->conn = kcp_conn_dial_with_options(actual_dial_addr, bind_ip, bind_device, options, packet_logger, logger, OMNI_NODE_ROLE_PEER, peer_id, stats_logger, stats_interval_ms);
|
||||
if (client->conn == NULL) {
|
||||
@@ -596,6 +625,9 @@ void kcp_client_state_snapshot(kcp_client_t *client, kcp_client_state_t *out_sta
|
||||
}
|
||||
pthread_mutex_lock(&client->state_mu);
|
||||
out_state->registered = client->registered;
|
||||
out_state->server_idle_ms = client->last_server_activity_ms == 0
|
||||
? 0
|
||||
: (omni_now_millis32() - client->last_server_activity_ms);
|
||||
snprintf(out_state->last_server_error, sizeof(out_state->last_server_error), "%s", client->last_server_error);
|
||||
pthread_mutex_unlock(&client->state_mu);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user