Browse Source

Publish bounded 100 ms send telemetry from the Android media actor

stream-multiview
evgeny 1 week ago
parent
commit
8b6c72b704
  1. 54
      tools/chatgui-android/libutun_lite/stream_bridge.c
  2. 8
      tools/chatgui-android/libutun_lite/stream_bridge.h

54
tools/chatgui-android/libutun_lite/stream_bridge.c

@ -9,6 +9,7 @@
#include "../../../src/stream/stream.h"
#include "../../../src/stream/stream_member.h"
#include "../../../src/stream/stream_media.h"
#include "../../../src/stream/stream_network.h"
#include "stream_bridge.h"
#define TX_LIMIT (2u * 1024u * 1024u)
@ -38,6 +39,9 @@ static pthread_mutex_t media_lock = PTHREAD_MUTEX_INITIALIZER;
static struct UTUN_INSTANCE *media_inst;
static struct posted_task *media_post;
static void *media_timer;
static void *stats_timer;
static struct android_stream_tx_stats tx_stats;
static uint64_t stats_report_us;
static struct android_media_session publisher, viewer;
static struct media_queue video_queue, audio_queue, output_queue;
static struct stream_media_status viewer_status;
@ -47,6 +51,41 @@ static int watch_pending;
static void media_process(void *arg);
static void media_close(struct android_media_session *s, int kind);
static void media_stats(void *arg) {
(void)arg; pthread_mutex_lock(&media_lock); stats_timer = NULL;
if (media_inst && publisher.handle && stream_handle_active(publisher.handle)) {
uint64_t now = get_time_us(); struct etcp_tx_snapshot transport; struct stream_info info;
if (etcp_tx_get_snapshot(media_inst, now, &transport) == 0 && stream_get_info(publisher.handle, &info) == STREAM_OK) {
tx_stats = (struct android_stream_tx_stats){.id = publisher.id, .sample_us = now,
.video_bytes = video_queue.bytes - (video_queue.head ? video_queue.head->offset : 0),
.audio_bytes = audio_queue.bytes, .stream_bytes = stream_network_pending_bytes(media_inst, &info.descriptor),
.transport_bytes = transport.queued_bytes, .inflight_bytes = transport.inflight_bytes,
.acked_1s = transport.acked_1s, .connections = transport.connections};
tx_stats.total_bytes = tx_stats.video_bytes + tx_stats.audio_bytes + tx_stats.stream_bytes
+ tx_stats.transport_bytes + tx_stats.inflight_bytes;
if (now - stats_report_us >= 1000000) {
stats_report_us = now;
DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "TX id=%llu video=%llu audio=%llu RS=%llu ETCP=%llu inflight=%llu total=%llu ACK_1s=%llu conns=%u",
(unsigned long long)tx_stats.id, (unsigned long long)tx_stats.video_bytes, (unsigned long long)tx_stats.audio_bytes,
(unsigned long long)tx_stats.stream_bytes, (unsigned long long)tx_stats.transport_bytes,
(unsigned long long)tx_stats.inflight_bytes, (unsigned long long)tx_stats.total_bytes,
(unsigned long long)tx_stats.acked_1s, tx_stats.connections);
}
}
stats_timer = uasync_set_timeout(media_inst->ua, 1000, NULL, media_stats, "stream_tx_stats");
if (!stats_timer) DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "TX stats timer allocation failed");
}
pthread_mutex_unlock(&media_lock);
}
int android_stream_tx_snapshot(uint64_t id, struct android_stream_tx_stats *out) {
if (!out) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "TX stats output missing"); return -1; }
pthread_mutex_lock(&media_lock);
int result = media_inst && publisher.id == id && tx_stats.id == id ? 0 : -1;
if (!result) *out = tx_stats;
pthread_mutex_unlock(&media_lock); return result;
}
/* Очереди обмена pthread: LL_QUEUE принадлежит uasync и сюда не подходит. */
static void queue_append(struct media_queue *q, struct media_record *record) {
record->next = NULL;
@ -110,6 +149,7 @@ static void media_output(uint64_t id, int kind, const struct stream_media_format
if (format) {
stream_write_be(p + 2, format->width, 2); stream_write_be(p + 4, format->height, 2);
stream_write_be(p + 6, format->rotation, 2); stream_write_be(p + 20, format->revision, 4);
p[24] = format->video_codec;
}
stream_write_be(p + 8, pts, 8); stream_write_be(p + 16, size, 4);
if (size) memcpy(p + ANDROID_STREAM_HEADER_SIZE, data, size);
@ -147,7 +187,10 @@ static void media_event(struct UTUN_INSTANCE *inst, const struct stream_event *e
static void media_close(struct android_media_session *s, int kind) {
if (kind) media_output(s->id, kind, NULL, 0, kind == 6 ? s->reason : 0, NULL, 0);
if (s->handle) stream_close(s->handle);
if (s == &publisher) { queue_clear(&video_queue); queue_clear(&audio_queue); }
if (s == &publisher) {
if (stats_timer) { uasync_cancel_timeout(media_inst->ua, stats_timer); stats_timer = NULL; }
memset(&tx_stats, 0, sizeof(tx_stats)); queue_clear(&video_queue); queue_clear(&audio_queue);
}
memset(s, 0, sizeof(*s));
}
@ -237,6 +280,10 @@ static void media_process(void *arg) {
media_close(s, 6);
} else {
if (s->source) { viewer_status_id = s->id; memset(&viewer_status, 0, sizeof(viewer_status)); s->want_read = 1; }
else {
stats_timer = uasync_set_timeout(media_inst->ua, 1000, NULL, media_stats, "stream_tx_stats");
if (!stats_timer) DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "TX stats start timer allocation failed");
}
media_output(s->id, 4, NULL, 0, 0, NULL, 0);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "Android stream opened id=%llu publisher=%d group=%llx source=%llx",
(unsigned long long)s->id, !s->source, (unsigned long long)s->group, (unsigned long long)s->source);
@ -386,7 +433,8 @@ int android_stream_feed(uint64_t id, int video, uint64_t pts, int key, const uin
int reconfigure = 0;
if (video) {
if (!format || !format->revision || !format->width || format->width > 4096 || !format->height || format->height > 4096
|| format->rotation > 270 || format->rotation % 90 || format->video_codec != STREAM_MEDIA_H264
|| format->rotation > 270 || format->rotation % 90
|| format->video_codec < STREAM_MEDIA_H264 || format->video_codec > STREAM_MEDIA_H266
|| format->audio_codec != publisher.format.audio_codec) {
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "Android video feed invalid configuration id=%llu", (unsigned long long)id); goto done;
}
@ -396,7 +444,7 @@ int android_stream_feed(uint64_t id, int video, uint64_t pts, int key, const uin
}
reconfigure = format->revision > publisher.format.revision;
if (reconfigure && !key) { DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "Android new revision=%u waits KEY", format->revision); result = 0; goto done; }
if (!reconfigure && (format->width != publisher.format.width || format->height != publisher.format.height
if (!reconfigure && (format->video_codec != publisher.format.video_codec || format->width != publisher.format.width || format->height != publisher.format.height
|| format->rotation != publisher.format.rotation)) {
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "Android video configuration changed without revision=%u", format->revision); goto done;
}

8
tools/chatgui-android/libutun_lite/stream_bridge.h

@ -14,7 +14,13 @@
struct UTUN_INSTANCE;
struct stream_media_format;
struct stream_media_status;
#define ANDROID_STREAM_HEADER_SIZE 24
#define ANDROID_STREAM_HEADER_SIZE 28
struct android_stream_tx_stats {
uint64_t id, sample_us, video_bytes, audio_bytes, stream_bytes, transport_bytes, inflight_bytes, acked_1s, total_bytes;
unsigned connections;
};
/* Снимок обновляется в uasync каждые 100ms; getter копирует его под bridge mutex. */
int android_stream_tx_snapshot(uint64_t id, struct android_stream_tx_stats *stats);
/* 0 attached, -1 invalid/busy: нельзя передать singleton другому ядру до detach старого. */
int android_stream_attach(struct UTUN_INSTANCE *inst);
void android_stream_detach(struct UTUN_INSTANCE *inst);

Loading…
Cancel
Save