|
|
|
|
@ -10,7 +10,7 @@
|
|
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
enum { VIDEO_CAPS = 1, VIDEO_KEY_REQUEST = 2, VIDEO_CAPS_ACK = 3 }; |
|
|
|
|
enum { VIDEO_CAPS = 1, VIDEO_KEY_REQUEST = 2, VIDEO_CAPS_ACK = 3, VIDEO_DECODE_OK = 4, VIDEO_DECODE_REJECT = 5 }; |
|
|
|
|
struct call_video_transport { |
|
|
|
|
struct call_session *session; |
|
|
|
|
struct call_video_caps local, peer; |
|
|
|
|
@ -29,6 +29,8 @@ struct call_video_transport {
|
|
|
|
|
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; |
|
|
|
|
uint64_t received_generation, decode_generation; |
|
|
|
|
int decode_result, decode_pending, tx_rejected; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static struct call_session *active(struct UTUN_INSTANCE *inst, uint64_t id) { |
|
|
|
|
@ -55,6 +57,7 @@ static int control_send(struct call_video_transport *v, uint8_t type) {
|
|
|
|
|
struct call_session *s = v->session; |
|
|
|
|
uint8_t data[17 + CALL_VIDEO_CAPS_BYTES]; data[0] = type; memcpy(data + 1, &s->call_id, 8); |
|
|
|
|
uint64_t revision = type == VIDEO_CAPS ? v->local_revision : type == VIDEO_CAPS_ACK ? v->peer_revision : 0; |
|
|
|
|
if (type == VIDEO_DECODE_OK || type == VIDEO_DECODE_REJECT) revision = v->decode_generation; |
|
|
|
|
memcpy(data + 9, &revision, 8); |
|
|
|
|
size_t length = 17; |
|
|
|
|
if (type == VIDEO_CAPS) { |
|
|
|
|
@ -82,7 +85,7 @@ static void update_rate(struct call_video_transport *v) {
|
|
|
|
|
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) { |
|
|
|
|
if (v->rate_pending && now - v->rate_requested_us > 12000000) { |
|
|
|
|
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_format.generation, (unsigned long long)v->rate_request); |
|
|
|
|
@ -173,7 +176,8 @@ static void tick(void *arg) {
|
|
|
|
|
// Отдельный control может обогнать ACCEPT: повторяем до подтверждения приложения.
|
|
|
|
|
if (v->local_revision && v->acknowledged_revision != v->local_revision && get_time_tb() - v->caps_sent_tb >= 5000) |
|
|
|
|
control_send(v, VIDEO_CAPS); |
|
|
|
|
update_rate(v); |
|
|
|
|
if (v->decode_pending && !control_send(v, v->decode_result ? VIDEO_DECODE_OK : VIDEO_DECODE_REJECT)) v->decode_pending = 0; |
|
|
|
|
if (!v->tx_rejected) update_rate(v); |
|
|
|
|
flush(v); |
|
|
|
|
v->timer = uasync_set_timeout(v->session->ctx->inst->ua, 500, v, tick, "call_video"); |
|
|
|
|
if (!v->timer) DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video timer rearm failed call=%llu", (unsigned long long)v->session->call_id); |
|
|
|
|
@ -233,6 +237,7 @@ int call_video_send_frame(struct UTUN_INSTANCE *inst, const struct call_video_fr
|
|
|
|
|
f ? f->size : 0, f ? f->offset : 0, f ? f->length : 0, f && call_video_format_valid(&f->format)); return -1; |
|
|
|
|
} |
|
|
|
|
if (!v->have_peer || v->acknowledged_revision != v->local_revision) { v->need_key = 1; return 0; } |
|
|
|
|
if (v->tx_rejected) return 0; |
|
|
|
|
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 profile=%u", |
|
|
|
|
f->format.codec, f->format.level, f->format.maximum_bps, f->format.profile); return -1; |
|
|
|
|
@ -284,7 +289,7 @@ int call_video_configure_rate(struct UTUN_INSTANCE *inst, uint64_t id, const str
|
|
|
|
|
(unsigned long long)id, range_minimum, maximum); return -1; |
|
|
|
|
} |
|
|
|
|
if (v->rate_format.generation != generation) { |
|
|
|
|
v->rate_format = *format; v->rate_pending = 0; v->admit = 1; |
|
|
|
|
v->rate_format = *format; v->rate_pending = 0; v->admit = 1; v->tx_rejected = 0; |
|
|
|
|
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; |
|
|
|
|
@ -323,6 +328,19 @@ void call_video_request_key(struct UTUN_INSTANCE *inst, uint64_t id) {
|
|
|
|
|
if (v->key_request_tb && now - v->key_request_tb < 5000) return; |
|
|
|
|
if (!control_send(v, VIDEO_KEY_REQUEST)) v->key_request_tb = now; |
|
|
|
|
} |
|
|
|
|
int call_video_decode_result(struct UTUN_INSTANCE *inst, uint64_t id, uint64_t generation, int accepted) { |
|
|
|
|
struct call_session *s = active(inst, id); |
|
|
|
|
struct call_video_transport *v = s ? s->video : NULL; |
|
|
|
|
if (!v || !generation || generation != v->received_generation || (accepted != 0 && accepted != 1)) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video stale decoder result call=%llu generation=%llu", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation); return -1; |
|
|
|
|
} |
|
|
|
|
v->decode_generation = generation; v->decode_result = accepted; v->decode_pending = 1; |
|
|
|
|
if (!control_send(v, accepted ? VIDEO_DECODE_OK : VIDEO_DECODE_REJECT)) v->decode_pending = 0; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video decoder result call=%llu generation=%llu accepted=%d pending=%d", |
|
|
|
|
(unsigned long long)id, (unsigned long long)generation, accepted, v->decode_pending); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
static int ready(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, const uint8_t *data, size_t length) { |
|
|
|
|
struct call_video_frame f; |
|
|
|
|
if (call_video_fragment_parse(data, length, &f)) return 1; |
|
|
|
|
@ -382,12 +400,19 @@ static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) {
|
|
|
|
|
} else if (data[0] == VIDEO_KEY_REQUEST && length == 17 && !revision) { |
|
|
|
|
v->need_key = 1; |
|
|
|
|
if (v->ops.need_key) v->ops.need_key(v->arg); |
|
|
|
|
} else if ((data[0] == VIDEO_DECODE_OK || data[0] == VIDEO_DECODE_REJECT) && length == 17 && revision) { |
|
|
|
|
if (revision != v->rate_format.generation) goto done; |
|
|
|
|
int accepted = data[0] == VIDEO_DECODE_OK; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video remote decoder result call=%llu generation=%llu accepted=%d", |
|
|
|
|
(unsigned long long)id, (unsigned long long)revision, accepted); |
|
|
|
|
if (!accepted) { v->tx_rejected = 1; v->admit = 0; if (v->data) discard_tx(v, "decoder_rejected"); } |
|
|
|
|
if (v->ops.decoded) v->ops.decoded(v->arg, revision, accepted); |
|
|
|
|
} else goto invalid; |
|
|
|
|
} else if (v->ops.frame) { |
|
|
|
|
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); |
|
|
|
|
if (result == 1) { v->received_generation = complete.format.generation; v->ops.frame(v->arg, &complete); } |
|
|
|
|
else if (result < 0 || (!(f.flags & CALL_VIDEO_KEY) && call_video_receiver_needs_key(v->receiver))) |
|
|
|
|
call_video_request_key(conn->instance, id); |
|
|
|
|
} |
|
|
|
|
|