fix: 断联视频堆积问题与控制命令失效问题
This commit is contained in:
@@ -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