|
|
|
@ -8,7 +8,7 @@ |
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
#include <string.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
|
|
enum { VIDEO_CAPS = 1, VIDEO_KEY_REQUEST = 2 }; |
|
|
|
enum { VIDEO_CAPS = 1, VIDEO_KEY_REQUEST = 2, VIDEO_CAPS_ACK = 3 }; |
|
|
|
struct call_video_transport { |
|
|
|
struct call_video_transport { |
|
|
|
struct call_session *session; |
|
|
|
struct call_session *session; |
|
|
|
struct call_video_caps local, peer; |
|
|
|
struct call_video_caps local, peer; |
|
|
|
@ -18,6 +18,7 @@ struct call_video_transport { |
|
|
|
struct call_video_frame tx; |
|
|
|
struct call_video_frame tx; |
|
|
|
uint8_t *data; |
|
|
|
uint8_t *data; |
|
|
|
uint64_t tx_started_tb, tx_last_id, key_request_tb; |
|
|
|
uint64_t tx_started_tb, tx_last_id, key_request_tb; |
|
|
|
|
|
|
|
uint64_t local_revision, peer_revision, acknowledged_revision; |
|
|
|
int64_t audio_end_pts, tx_last_pts; |
|
|
|
int64_t audio_end_pts, tx_last_pts; |
|
|
|
int have_peer, need_key; |
|
|
|
int have_peer, need_key; |
|
|
|
}; |
|
|
|
}; |
|
|
|
@ -43,10 +44,12 @@ static struct call_video_transport *ensure(struct call_session *s) { |
|
|
|
} |
|
|
|
} |
|
|
|
static int control_send(struct call_video_transport *v, uint8_t type) { |
|
|
|
static int control_send(struct call_video_transport *v, uint8_t type) { |
|
|
|
struct call_session *s = v->session; |
|
|
|
struct call_session *s = v->session; |
|
|
|
uint8_t data[9 + CALL_VIDEO_CAPS_BYTES]; data[0] = type; memcpy(data + 1, &s->call_id, 8); |
|
|
|
uint8_t data[17 + CALL_VIDEO_CAPS_BYTES]; data[0] = type; memcpy(data + 1, &s->call_id, 8); |
|
|
|
size_t length = 9; |
|
|
|
uint64_t revision = type == VIDEO_CAPS ? v->local_revision : type == VIDEO_CAPS_ACK ? v->peer_revision : 0; |
|
|
|
|
|
|
|
memcpy(data + 9, &revision, 8); |
|
|
|
|
|
|
|
size_t length = 17; |
|
|
|
if (type == VIDEO_CAPS) { |
|
|
|
if (type == VIDEO_CAPS) { |
|
|
|
if (call_video_caps_encode(&v->local, data + 9)) return -1; |
|
|
|
if (call_video_caps_encode(&v->local, data + 17)) return -1; |
|
|
|
length += CALL_VIDEO_CAPS_BYTES; |
|
|
|
length += CALL_VIDEO_CAPS_BYTES; |
|
|
|
} |
|
|
|
} |
|
|
|
struct ETCP_ROUTER_CONN *conn = etcp_router_conn_get(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_VIDEO_CONTROL); |
|
|
|
struct ETCP_ROUTER_CONN *conn = etcp_router_conn_get(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_VIDEO_CONTROL); |
|
|
|
@ -65,6 +68,7 @@ static void flush(struct call_video_transport *v) { |
|
|
|
if (!v->data) return; |
|
|
|
if (!v->data) return; |
|
|
|
struct call_session *s = v->session; |
|
|
|
struct call_session *s = v->session; |
|
|
|
if (get_time_tb() - v->tx_started_tb > 20000) { discard_tx(v, "deadline"); return; } |
|
|
|
if (get_time_tb() - v->tx_started_tb > 20000) { discard_tx(v, "deadline"); return; } |
|
|
|
|
|
|
|
if (v->acknowledged_revision != v->local_revision) return; |
|
|
|
// Видео не обгоняет соответствующий аудиокадр. Отказ камеры не задерживает звук.
|
|
|
|
// Видео не обгоняет соответствующий аудиокадр. Отказ камеры не задерживает звук.
|
|
|
|
if (v->tx.pts_us > v->audio_end_pts) return; |
|
|
|
if (v->tx.pts_us > v->audio_end_pts) return; |
|
|
|
struct ETCP_ROUTER_CONN *conn = etcp_router_conn_get(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_VIDEO); |
|
|
|
struct ETCP_ROUTER_CONN *conn = etcp_router_conn_get(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_VIDEO); |
|
|
|
@ -113,6 +117,9 @@ int call_video_configure(struct UTUN_INSTANCE *inst, uint64_t id, const struct c |
|
|
|
struct call_video_transport *v = ensure(s); |
|
|
|
struct call_video_transport *v = ensure(s); |
|
|
|
if (!v) return -1; |
|
|
|
if (!v) return -1; |
|
|
|
v->local = *caps; v->ops = ops ? *ops : (struct call_video_ops){0}; v->arg = arg; |
|
|
|
v->local = *caps; v->ops = ops ? *ops : (struct call_video_ops){0}; v->arg = arg; |
|
|
|
|
|
|
|
if (++v->local_revision == 0) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video capabilities revision exhausted"); return -1; |
|
|
|
|
|
|
|
} |
|
|
|
if (!ops) { |
|
|
|
if (!ops) { |
|
|
|
memset(&v->local, 0, sizeof(v->local)); |
|
|
|
memset(&v->local, 0, sizeof(v->local)); |
|
|
|
if (v->data) discard_tx(v, "platform_detached"); |
|
|
|
if (v->data) discard_tx(v, "platform_detached"); |
|
|
|
@ -132,7 +139,8 @@ int call_video_send_frame(struct UTUN_INSTANCE *inst, const struct call_video_fr |
|
|
|
|| (f->flags & ~CALL_VIDEO_KEY) || f->frame_id <= v->tx_last_id || f->pts_us <= v->tx_last_pts) { |
|
|
|
|| (f->flags & ~CALL_VIDEO_KEY) || f->frame_id <= v->tx_last_id || f->pts_us <= v->tx_last_pts) { |
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX invalid frame/session"); return -1; |
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX invalid frame/session"); return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!v->have_peer || call_video_choose(&v->local, &v->peer, f->format.codec, f->format.width, f->format.height, f->format.fps) |
|
|
|
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; } |
|
|
|
!= f->format.codec) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX unsupported peer format"); return -1; } |
|
|
|
if (v->data) { v->need_key = 1; return 0; } |
|
|
|
if (v->data) { v->need_key = 1; return 0; } |
|
|
|
int changed = !call_video_same_format(&f->format, &v->tx.format); |
|
|
|
int changed = !call_video_same_format(&f->format, &v->tx.format); |
|
|
|
@ -165,6 +173,8 @@ 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_session *s = active(inst, f.call_id); |
|
|
|
struct call_video_transport *v = s ? s->video : NULL; |
|
|
|
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 || 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; |
|
|
|
return v->ops.receive_ready(v->arg, f.size); |
|
|
|
return v->ops.receive_ready(v->arg, f.size); |
|
|
|
} |
|
|
|
} |
|
|
|
static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
@ -179,7 +189,7 @@ static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
int control = entry->dgram[0] == ETCP_RT_ID_CALL_VIDEO_CONTROL; |
|
|
|
int control = entry->dgram[0] == ETCP_RT_ID_CALL_VIDEO_CONTROL; |
|
|
|
struct call_video_frame f; |
|
|
|
struct call_video_frame f; |
|
|
|
if (control) { |
|
|
|
if (control) { |
|
|
|
if (length != 9 && length != 9 + CALL_VIDEO_CAPS_BYTES) goto invalid; |
|
|
|
if (length != 17 && length != 17 + CALL_VIDEO_CAPS_BYTES) goto invalid; |
|
|
|
memcpy(&id, data + 1, 8); |
|
|
|
memcpy(&id, data + 1, 8); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (call_video_fragment_parse(data, length, &f)) goto done; |
|
|
|
if (call_video_fragment_parse(data, length, &f)) goto done; |
|
|
|
@ -191,14 +201,25 @@ static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
struct call_video_transport *v = ensure(s); |
|
|
|
struct call_video_transport *v = ensure(s); |
|
|
|
if (!v) goto done; |
|
|
|
if (!v) goto done; |
|
|
|
if (control) { |
|
|
|
if (control) { |
|
|
|
if (data[0] == VIDEO_CAPS && length == 9 + CALL_VIDEO_CAPS_BYTES) { |
|
|
|
uint64_t revision; memcpy(&revision, data + 9, 8); |
|
|
|
|
|
|
|
if (data[0] == VIDEO_CAPS && length == 17 + CALL_VIDEO_CAPS_BYTES && revision) { |
|
|
|
struct call_video_caps caps; |
|
|
|
struct call_video_caps caps; |
|
|
|
if (call_video_caps_parse(data + 9, CALL_VIDEO_CAPS_BYTES, &caps)) goto done; |
|
|
|
if (call_video_caps_parse(data + 17, CALL_VIDEO_CAPS_BYTES, &caps)) goto done; |
|
|
|
|
|
|
|
if (revision <= v->peer_revision) { |
|
|
|
|
|
|
|
if (revision == v->peer_revision) control_send(v, VIDEO_CAPS_ACK); |
|
|
|
|
|
|
|
goto done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
v->peer_revision = revision; |
|
|
|
v->peer = caps; v->have_peer = 1; |
|
|
|
v->peer = caps; v->have_peer = 1; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video peer capabilities call=%llu encode=%u/%u decode=%u/%u", (unsigned long long)id, |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video peer capabilities call=%llu encode=%u/%u decode=%u/%u", (unsigned long long)id, |
|
|
|
caps.encode[0].width, caps.encode[1].width, caps.decode[0].width, caps.decode[1].width); |
|
|
|
caps.encode[0].width, caps.encode[1].width, caps.decode[0].width, caps.decode[1].width); |
|
|
|
if (v->ops.capabilities) v->ops.capabilities(v->arg, &v->peer); |
|
|
|
if (v->ops.capabilities) v->ops.capabilities(v->arg, &v->peer); |
|
|
|
} else if (data[0] == VIDEO_KEY_REQUEST && length == 9) { |
|
|
|
control_send(v, VIDEO_CAPS_ACK); |
|
|
|
|
|
|
|
} else if (data[0] == VIDEO_CAPS_ACK && length == 17 && revision && revision == v->local_revision) { |
|
|
|
|
|
|
|
v->acknowledged_revision = revision; |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video capabilities acknowledged call=%llu revision=%llu", |
|
|
|
|
|
|
|
(unsigned long long)id, (unsigned long long)revision); |
|
|
|
|
|
|
|
} else if (data[0] == VIDEO_KEY_REQUEST && length == 17 && !revision) { |
|
|
|
v->need_key = 1; |
|
|
|
v->need_key = 1; |
|
|
|
if (v->ops.need_key) v->ops.need_key(v->arg); |
|
|
|
if (v->ops.need_key) v->ops.need_key(v->arg); |
|
|
|
} else goto invalid; |
|
|
|
} else goto invalid; |
|
|
|
@ -208,7 +229,8 @@ static void receive(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
struct call_video_frame complete; |
|
|
|
struct call_video_frame complete; |
|
|
|
int result = call_video_receiver_push(v->receiver, &f, get_time_us(), &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->ops.frame(v->arg, &complete); |
|
|
|
else if (call_video_receiver_needs_key(v->receiver)) call_video_request_key(conn->instance, id); |
|
|
|
else if (result < 0 || (!(f.flags & CALL_VIDEO_KEY) && call_video_receiver_needs_key(v->receiver))) |
|
|
|
|
|
|
|
call_video_request_key(conn->instance, id); |
|
|
|
} |
|
|
|
} |
|
|
|
goto done; |
|
|
|
goto done; |
|
|
|
invalid: |
|
|
|
invalid: |
|
|
|
|