|
|
|
|
@ -25,7 +25,8 @@ struct call_video_transport {
|
|
|
|
|
int have_peer, need_key; |
|
|
|
|
struct call_video_rate rate; |
|
|
|
|
struct route_tx_meter production; |
|
|
|
|
uint64_t rate_generation, rate_request, produced_id, route_local_epoch, route_peer_epoch, rate_requested_us; |
|
|
|
|
struct call_video_format rate_format; |
|
|
|
|
uint64_t rate_request, produced_id, route_local_epoch, route_peer_epoch, rate_requested_us; |
|
|
|
|
uint32_t applied_bps, maximum_bps, range_minimum_bps, range_maximum_bps; |
|
|
|
|
int rate_pending, admit; |
|
|
|
|
}; |
|
|
|
|
@ -78,15 +79,20 @@ static uint64_t media_bytes(uint64_t bytes, uint64_t packets) {
|
|
|
|
|
return bytes >= headers ? bytes - headers : 0; |
|
|
|
|
} |
|
|
|
|
static void update_rate(struct call_video_transport *v) { |
|
|
|
|
if (!v->rate_generation || !v->applied_bps) return; |
|
|
|
|
if (!v->rate_format.generation || !v->applied_bps) return; |
|
|
|
|
struct call_session *s = v->session; |
|
|
|
|
uint64_t now = get_time_us(); |
|
|
|
|
if (v->rate_pending && now - v->rate_requested_us > 2000000) { |
|
|
|
|
v->rate_pending = 0; v->applied_bps = 0; v->admit = 0; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video encoder command timeout call=%llu generation=%llu request=%llu", |
|
|
|
|
(unsigned long long)s->call_id, (unsigned long long)v->rate_generation, (unsigned long long)v->rate_request); |
|
|
|
|
(unsigned long long)s->call_id, (unsigned long long)v->rate_format.generation, (unsigned long long)v->rate_request); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (!v->have_peer || !call_video_format_supported(&v->local, &v->peer, &v->rate_format)) { |
|
|
|
|
if (v->admit) DEBUG_WARN(DEBUG_CATEGORY_CALL, "video rate mode no longer supported call=%llu generation=%llu; admission paused", |
|
|
|
|
(unsigned long long)s->call_id, (unsigned long long)v->rate_format.generation); |
|
|
|
|
v->admit = 0; return; |
|
|
|
|
} |
|
|
|
|
uint32_t minimum, maximum; |
|
|
|
|
if (call_video_rate_parse_range(chat_setting_get(s->ctx->inst, "call_video_bitrate_range_kbps"), &minimum, &maximum)) { |
|
|
|
|
v->admit = 0; return; |
|
|
|
|
@ -131,13 +137,16 @@ static void update_rate(struct call_video_transport *v) {
|
|
|
|
|
v->admit = result.admit; |
|
|
|
|
if (result.target_bps != v->applied_bps && v->ops.bitrate) { |
|
|
|
|
v->rate_pending = 1; ++v->rate_request; v->rate_requested_us = now; |
|
|
|
|
v->ops.bitrate(v->arg, v->rate_generation, v->rate_request, result.target_bps); |
|
|
|
|
v->ops.bitrate(v->arg, v->rate_format.generation, v->rate_request, result.target_bps); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
static void flush(struct call_video_transport *v) { |
|
|
|
|
if (!v->data) return; |
|
|
|
|
struct call_session *s = v->session; |
|
|
|
|
if (get_time_tb() - v->tx_started_tb > 20000) { discard_tx(v, "deadline"); return; } |
|
|
|
|
if (!v->have_peer || !call_video_format_supported(&v->local, &v->peer, &v->tx.format)) { |
|
|
|
|
discard_tx(v, "capabilities_changed"); return; |
|
|
|
|
} |
|
|
|
|
if (v->acknowledged_revision != v->local_revision) return; |
|
|
|
|
// Видео не обгоняет соответствующий аудиокадр. Отказ камеры не задерживает звук.
|
|
|
|
|
if (v->tx.pts_us > v->audio_end_pts) return; |
|
|
|
|
@ -197,6 +206,7 @@ int call_video_configure(struct UTUN_INSTANCE *inst, uint64_t id, const struct c
|
|
|
|
|
} |
|
|
|
|
if (!ops) { |
|
|
|
|
memset(&v->local, 0, sizeof(v->local)); |
|
|
|
|
v->rate_format = (struct call_video_format){0}; v->applied_bps = 0; v->rate_pending = 0; v->admit = 0; |
|
|
|
|
if (v->data) discard_tx(v, "platform_detached"); |
|
|
|
|
call_video_receiver_reset(v->receiver); |
|
|
|
|
} |
|
|
|
|
@ -215,10 +225,14 @@ int call_video_send_frame(struct UTUN_INSTANCE *inst, const struct call_video_fr
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX invalid frame/session"); return -1; |
|
|
|
|
} |
|
|
|
|
if (!v->have_peer || v->acknowledged_revision != v->local_revision) { v->need_key = 1; return 0; } |
|
|
|
|
if (call_video_choose(&v->local, &v->peer, f->format.codec, f->format.width, f->format.height, f->format.fps) |
|
|
|
|
!= f->format.codec) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX unsupported peer format"); return -1; } |
|
|
|
|
if (v->rate_generation) { |
|
|
|
|
if (f->format.generation != v->rate_generation) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX stale encoder generation"); return -1; } |
|
|
|
|
if (!call_video_format_supported(&v->local, &v->peer, &f->format)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX unsupported format codec=%u level=%u bitrate_cap=%u vbv=%u", |
|
|
|
|
f->format.codec, f->format.level, f->format.maximum_bps, f->format.vbv_bits); return -1; |
|
|
|
|
} |
|
|
|
|
if (v->rate_format.generation) { |
|
|
|
|
if (!call_video_same_format(&f->format, &v->rate_format)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX differs from configured encoder generation/mode"); return -1; |
|
|
|
|
} |
|
|
|
|
if (f->frame_id > v->produced_id) { |
|
|
|
|
route_tx_meter_ack(&v->production, get_time_us(), f->size); v->produced_id = f->frame_id; |
|
|
|
|
} |
|
|
|
|
@ -242,12 +256,16 @@ int call_video_send_frame(struct UTUN_INSTANCE *inst, const struct call_video_fr
|
|
|
|
|
v->tx_started_tb = get_time_tb(); v->tx_last_id = f->frame_id; v->tx_last_pts = f->pts_us; v->need_key = 0; |
|
|
|
|
flush(v); return 1; |
|
|
|
|
} |
|
|
|
|
int call_video_configure_rate(struct UTUN_INSTANCE *inst, uint64_t id, uint64_t generation, |
|
|
|
|
int call_video_configure_rate(struct UTUN_INSTANCE *inst, uint64_t id, const struct call_video_format *format, |
|
|
|
|
uint32_t applied, uint32_t maximum) { |
|
|
|
|
struct call_session *s = active(inst, id); |
|
|
|
|
struct call_video_transport *v = s ? s->video : NULL; |
|
|
|
|
if (!v || !v->ops.bitrate || !generation || generation < v->rate_generation || applied < 64000 || applied > 16000000 |
|
|
|
|
|| maximum < 64000 || maximum > 16000000) { |
|
|
|
|
uint64_t generation = format ? format->generation : 0; |
|
|
|
|
if (!v || !v->ops.bitrate || !v->have_peer || !call_video_format_supported(&v->local, &v->peer, format) |
|
|
|
|
|| generation < v->rate_format.generation |
|
|
|
|
|| (generation == v->rate_format.generation && !call_video_same_format(format, &v->rate_format)) |
|
|
|
|
|| applied < CALL_VIDEO_RATE_MIN_BPS || applied > format->maximum_bps |
|
|
|
|
|| maximum < CALL_VIDEO_RATE_MIN_BPS || maximum > format->maximum_bps) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video rate configure invalid call=%llu generation=%llu bitrate=%u cap=%u", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation, applied, maximum); return -1; |
|
|
|
|
} |
|
|
|
|
@ -257,22 +275,23 @@ int call_video_configure_rate(struct UTUN_INSTANCE *inst, uint64_t id, uint64_t
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video rate codec cap below requested minimum call=%llu min=%u cap=%u", |
|
|
|
|
(unsigned long long)id, range_minimum, maximum); return -1; |
|
|
|
|
} |
|
|
|
|
if (v->rate_generation != generation) { |
|
|
|
|
v->rate_generation = generation; v->rate_pending = 0; v->admit = 1; |
|
|
|
|
if (v->rate_format.generation != generation) { |
|
|
|
|
v->rate_format = *format; v->rate_pending = 0; v->admit = 1; |
|
|
|
|
call_video_rate_init(&v->rate, get_time_us()); route_tx_meter_reset(&v->production, get_time_us()); |
|
|
|
|
} |
|
|
|
|
v->applied_bps = applied; v->maximum_bps = maximum; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video rate configured call=%llu generation=%llu applied=%u cap=%u", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation, applied, maximum); return 0; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video rate configured call=%llu generation=%llu codec=%u level=%u applied=%u cap=%u envelope=%u vbv=%u", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation, format->codec, format->level, |
|
|
|
|
applied, maximum, format->maximum_bps, format->vbv_bits); return 0; |
|
|
|
|
} |
|
|
|
|
int call_video_rate_applied(struct UTUN_INSTANCE *inst, uint64_t id, uint64_t generation, uint64_t request, uint32_t bps) { |
|
|
|
|
struct call_session *s = active(inst, id); |
|
|
|
|
struct call_video_transport *v = s ? s->video : NULL; |
|
|
|
|
if (!v || generation != v->rate_generation || request != v->rate_request || !v->rate_pending) { |
|
|
|
|
if (!v || generation != v->rate_format.generation || request != v->rate_request || !v->rate_pending) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video rate stale completion call=%llu generation=%llu request=%llu", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation, (unsigned long long)request); return -1; |
|
|
|
|
} |
|
|
|
|
if (bps && (bps < 64000 || bps > 16000000)) { |
|
|
|
|
if (bps && (bps < CALL_VIDEO_RATE_MIN_BPS || bps > v->rate_format.maximum_bps)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video rate invalid encoder result bps=%u", bps); return -1; |
|
|
|
|
} |
|
|
|
|
v->rate_pending = 0; v->applied_bps = bps; |
|
|
|
|
@ -302,8 +321,7 @@ static int ready(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, cons
|
|
|
|
|
struct call_session *s = active(inst, f.call_id); |
|
|
|
|
struct call_video_transport *v = s ? s->video : NULL; |
|
|
|
|
if (!v || s->group_id != group || s->peer_node_id != peer || !v->ops.frame || f.offset) return 1; |
|
|
|
|
if (!v->have_peer || call_video_choose(&v->peer, &v->local, f.format.codec, f.format.width, f.format.height, f.format.fps) |
|
|
|
|
!= f.format.codec) return 1; |
|
|
|
|
if (!v->have_peer || !call_video_format_supported(&v->peer, &v->local, &f.format)) return 1; |
|
|
|
|
return v->ops.receive_ready(v->arg, f.size); |
|
|
|
|
} |
|
|
|
|
static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
|
@ -354,8 +372,7 @@ static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) {
|
|
|
|
|
if (v->ops.need_key) v->ops.need_key(v->arg); |
|
|
|
|
} else goto invalid; |
|
|
|
|
} else if (v->ops.frame) { |
|
|
|
|
if (!v->have_peer || call_video_choose(&v->peer, &v->local, f.format.codec, f.format.width, f.format.height, f.format.fps) |
|
|
|
|
!= f.format.codec) goto invalid; |
|
|
|
|
if (!v->have_peer || !call_video_format_supported(&v->peer, &v->local, &f.format)) goto invalid; |
|
|
|
|
struct call_video_frame complete; |
|
|
|
|
int result = call_video_receiver_push(v->receiver, &f, get_time_us(), &complete); |
|
|
|
|
if (result == 1) v->ops.frame(v->arg, &complete); |
|
|
|
|
|