|
|
|
|
@ -26,6 +26,7 @@ static struct {
|
|
|
|
|
uint32_t minimum, maximum; |
|
|
|
|
struct call_video_frame tx[CALL_VIDEO_PORT_TX_FRAMES], rx[8]; |
|
|
|
|
unsigned tx_count; |
|
|
|
|
int tx_processing; |
|
|
|
|
unsigned count, bytes; |
|
|
|
|
uint64_t decoded_generation; |
|
|
|
|
int decoded_pending, decoded_accepted; |
|
|
|
|
@ -86,7 +87,15 @@ static void decoded(void *arg, uint64_t generation, int accepted) {
|
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&lock); |
|
|
|
|
} |
|
|
|
|
static const struct call_video_ops ops = {peer, ready, frame, need_key, bitrate, decoded}; |
|
|
|
|
static void writable(void *arg, int admit) { |
|
|
|
|
pthread_mutex_lock(&lock); |
|
|
|
|
if (port.id == (uint64_t)(uintptr_t)arg) { |
|
|
|
|
port.status.can_encode = admit; |
|
|
|
|
if (port.tx_count && !port.tx_processing) schedule(); |
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&lock); |
|
|
|
|
} |
|
|
|
|
static const struct call_video_ops ops = {peer, ready, frame, need_key, bitrate, decoded, writable}; |
|
|
|
|
|
|
|
|
|
/* Снимок команды под mutex; API транспорта вызывается без mutex, callbacks могут войти обратно. */ |
|
|
|
|
static void process(void *arg) { |
|
|
|
|
@ -106,7 +115,7 @@ static void process(void *arg) {
|
|
|
|
|
uint64_t generation = port.applied_generation, request = port.applied_request; |
|
|
|
|
struct call_video_frame tx[CALL_VIDEO_PORT_TX_FRAMES]; |
|
|
|
|
unsigned tx_count = port.tx_count; |
|
|
|
|
memcpy(tx, port.tx, tx_count * sizeof(*tx)); port.tx_count = 0; |
|
|
|
|
memcpy(tx, port.tx, tx_count * sizeof(*tx)); port.tx_count = 0; port.tx_processing = tx_count != 0; |
|
|
|
|
port.opening = port.configure = port.completed = port.resume = port.key = 0; |
|
|
|
|
port.range = 0; |
|
|
|
|
port.decoded_pending = 0; |
|
|
|
|
@ -147,19 +156,25 @@ static void process(void *arg) {
|
|
|
|
|
sent = call_video_send_frame(inst, &tx[sent_count]); |
|
|
|
|
if (sent != 1) break; |
|
|
|
|
} |
|
|
|
|
if (sent_count < tx_count) |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video port TX frames released call=%llu accepted=%u unsent=%u result=%d " |
|
|
|
|
if (sent_count < tx_count && sent < 0) |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video port TX chain failed call=%llu accepted=%u unsent=%u result=%d " |
|
|
|
|
"first_unsent=%llu last=%llu", (unsigned long long)id, sent_count, tx_count - sent_count, sent, |
|
|
|
|
(unsigned long long)tx[sent_count].frame_id, (unsigned long long)tx[tx_count - 1].frame_id); |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < tx_count; ++i) u_free((void *)tx[i].data); |
|
|
|
|
pthread_mutex_lock(&lock); |
|
|
|
|
port.tx_processing = 0; |
|
|
|
|
if (port.id == id) { |
|
|
|
|
port.status.minimum_bps = minimum; port.status.maximum_bps = maximum; |
|
|
|
|
if (result < 0) { port.status.error = 1; DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video port configuration failed call=%llu", (unsigned long long)id); } |
|
|
|
|
else if (configure) { port.status.configured_generation = format.generation; port.status.error = 0; } |
|
|
|
|
if (sent != 1) ++port.status.key_serial; |
|
|
|
|
if (!sent && active) { |
|
|
|
|
port.tx_count = tx_count - sent_count; |
|
|
|
|
memcpy(port.tx, tx + sent_count, port.tx_count * sizeof(*tx)); |
|
|
|
|
} |
|
|
|
|
if (sent < 0) ++port.status.key_serial; |
|
|
|
|
} |
|
|
|
|
unsigned release = !sent && active && port.id == id ? sent_count : tx_count; |
|
|
|
|
for (unsigned i = 0; i < release; ++i) u_free((void *)tx[i].data); |
|
|
|
|
pthread_mutex_unlock(&lock); |
|
|
|
|
} |
|
|
|
|
int call_video_port_attach(struct UTUN_INSTANCE *inst) { |
|
|
|
|
@ -205,7 +220,7 @@ void call_video_port_close(uint64_t id) {
|
|
|
|
|
int call_video_port_status(uint64_t id, struct call_video_port_status *out) { |
|
|
|
|
pthread_mutex_lock(&lock); |
|
|
|
|
int result = id && id == port.id && port.inst && out ? 0 : -1; |
|
|
|
|
if (!result) *out = port.status; |
|
|
|
|
if (!result) { *out = port.status; out->can_encode = out->can_encode && !port.tx_count && !port.tx_processing; } |
|
|
|
|
pthread_mutex_unlock(&lock); return result; |
|
|
|
|
} |
|
|
|
|
int call_video_port_format(uint64_t id, const struct call_video_format *format, uint32_t bps) { |
|
|
|
|
@ -237,7 +252,7 @@ int call_video_port_send(const struct call_video_frame *frames, unsigned count)
|
|
|
|
|
} |
|
|
|
|
pthread_mutex_lock(&lock); |
|
|
|
|
int result = frames[0].call_id && frames[0].call_id == port.id && port.inst ? 0 : -1; |
|
|
|
|
if (!result && !port.tx_count) { |
|
|
|
|
if (!result && !port.tx_count && !port.tx_processing) { |
|
|
|
|
unsigned copied = 0; |
|
|
|
|
for (; copied < count; ++copied) { |
|
|
|
|
uint8_t *data = u_malloc(frames[copied].size); |
|
|
|
|
@ -253,9 +268,9 @@ int call_video_port_send(const struct call_video_frame *frames, unsigned count)
|
|
|
|
|
result = -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (result == 0) DEBUG_WARN(DEBUG_CATEGORY_CALL, |
|
|
|
|
"video port TX mailbox busy call=%llu frames=%u first=%llu pending=%u; batch not queued", |
|
|
|
|
(unsigned long long)frames[0].call_id, count, (unsigned long long)frames[0].frame_id, port.tx_count); |
|
|
|
|
if (result == 0) DEBUG_TRACE(DEBUG_CATEGORY_CALL, |
|
|
|
|
"video port TX retry call=%llu frames=%u first=%llu pending=%u processing=%d", |
|
|
|
|
(unsigned long long)frames[0].call_id, count, (unsigned long long)frames[0].frame_id, port.tx_count, port.tx_processing); |
|
|
|
|
pthread_mutex_unlock(&lock); return result; |
|
|
|
|
} |
|
|
|
|
int call_video_port_pull(uint64_t id, struct call_video_frame *out) { |
|
|
|
|
|