Files
utun2/src/stream/stream_wire.h
T
evgeny 0e17756e19 Show stream playback buffer chains on desktop and Android
Send coalesced per-hop telemetry every 200 ms ahead of video DATA using the shared ETCP transport. Measure pending frames before ETCP handoff, retain stale and unavailable node values, and display the worst path with playback tempo.

Bump stream wire protocol to v7. Validate with 187 core tests, 21 desktop player tests, two Android indicator tests, and an Android APK build.
2026-10-25 15:21:23 +01:00

95 lines
6.1 KiB
C

/* Прямой stream-протокол: канонический big-endian заголовок, пути и подписанный END.
* IDs ETCP определены только в etcp_api.h. Указатели parser заимствованы до освобождения кодограммы. */
#ifndef STREAM_WIRE_H
#define STREAM_WIRE_H
#include "stream_proto.h"
#include "stream_metrics.h"
#ifdef __cplusplus
extern "C" {
#endif
#define STREAM_WIRE_VERSION 7
#define STREAM_WIRE_HEADER 52
#define STREAM_PATH_MAX 32
#define STREAM_END_SIZE 106
#define STREAM_METRIC_WIRE_SIZE 49
#define STREAM_INFO_METRIC_OFF 36
#define STREAM_INFO_OFFER_OFF (STREAM_INFO_METRIC_OFF + STREAM_METRIC_WIRE_SIZE)
#define STREAM_INFO_KEY_OFF (STREAM_INFO_OFFER_OFF + 25)
#define STREAM_INFO_PATH_OFF (STREAM_INFO_KEY_OFF + 16)
#define STREAM_INFO_SIZE (STREAM_INFO_PATH_OFF + 1) /* probe:8, границы/FEC:28, метрики:49, предложение:25, KEY:16, count:1 */
#define STREAM_OVERLOAD_MAX 64
#define STREAM_OVERLOAD_HEADER 18
#define STREAM_FRAME_TIMING_SIZE 48
#define STREAM_TELEMETRY_PERIOD_MS 200
#define STREAM_TELEMETRY_STALE_MS 400
#define STREAM_TELEMETRY_ROW_SIZE 33
enum stream_command {
STREAM_CMD_INFO_REQ = 1, STREAM_CMD_INFO_RESP, STREAM_CMD_SUBSCRIBE, STREAM_CMD_ACCEPT,
STREAM_CMD_REJECT, STREAM_CMD_RENEW, STREAM_CMD_UNSUBSCRIBE, STREAM_CMD_CLOSED,
STREAM_CMD_DATA, STREAM_CMD_GAP, STREAM_CMD_END, STREAM_CMD_REVERSE_REQ, STREAM_CMD_OVERLOAD, STREAM_CMD_OVERLOAD_SELECT,
STREAM_CMD_RELAY_REQUEST, STREAM_CMD_RELAY_STATUS, STREAM_CMD_RELAY_CANCEL, STREAM_CMD_TELEMETRY
};
/* Routed помощь: handle — nonce запроса; эпохи запроса/сервиса независимы от raw ETCP-сессии. */
enum stream_relay_status { STREAM_RELAY_STARTING = 1, STREAM_RELAY_READY, STREAM_RELAY_BOUND,
STREAM_RELAY_REJECTED, STREAM_RELAY_ENDED, STREAM_RELAY_CANCELLED };
enum stream_reject {
STREAM_REASON_STOPPED = 1, STREAM_REASON_POSITION, STREAM_REASON_EXPIRED, STREAM_REASON_STALE,
STREAM_REASON_CONFLICT, STREAM_REASON_LIMIT, STREAM_REASON_LOOP, STREAM_REASON_TRANSPORT,
STREAM_REASON_LEASE, STREAM_REASON_MEMBER, STREAM_REASON_PROTOCOL, STREAM_REASON_OVERLOAD
};
struct stream_path { uint64_t nodes[STREAM_PATH_MAX]; uint8_t count; };
/* Подсказка источника для учёта кадров без сборки FEC; только диагностика, не управление воспроизведением.
* started — начала кадров в пачке; continuation исключает повторный учёт кадра на границе пачек. */
struct stream_frame_timing {
uint64_t first_frame, last_frame, first_pts, last_pts;
uint32_t period_us, started, audio_us;
uint8_t valid, video, continuation;
};
enum stream_telemetry_flags { STREAM_TELEMETRY_KNOWN = 1, STREAM_TELEMETRY_STALE = 2, STREAM_TELEMETRY_DOWN = 4 };
struct stream_telemetry_row {
uint64_t node, buffered_us, sample; // Узел, длительность очереди материала и номер его измерения
uint32_t frames, age_ms; // Кадры и накопленный возраст измерения; пересылка его не обнуляет
uint8_t flags; // KNOWN, STALE, DOWN; неизвестное значение отличается от нуля
};
struct stream_telemetry {
struct stream_telemetry_row rows[STREAM_PATH_MAX]; // Фактический stream-путь до отправителя включительно
uint64_t received_ms; // Локальное монотонное время сохранения; по сети не передаётся
uint8_t count;
};
/* Канонические кодировки подсказки DATA и отдельного TELEMETRY; read проверяет полный размер и поля. */
void stream_frame_timing_write(uint8_t *wire, const struct stream_frame_timing *timing);
int stream_frame_timing_read(const uint8_t *wire, size_t length, struct stream_frame_timing *timing);
size_t stream_telemetry_write(uint8_t *wire, const struct stream_telemetry *telemetry);
int stream_telemetry_read(const uint8_t *wire, size_t length, struct stream_telemetry *telemetry);
/* Копия стареет по локальным часам; сумма служит только выбору худшего реального пути. */
void stream_telemetry_age(struct stream_telemetry *telemetry, uint64_t now_ms);
uint64_t stream_telemetry_total(const struct stream_telemetry *telemetry);
struct stream_message {
struct stream_descriptor descriptor;
uint64_t sender_epoch, receiver_epoch, handle;
uint8_t command, subblock;
const uint8_t *payload;
size_t length;
};
/* Проверяет размер, версию, ненулевые ключи/epoch и известный command; payload разбирает диспетчер. */
int stream_wire_parse(const uint8_t *wire, size_t length, struct stream_message *message);
void stream_wire_header(uint8_t *wire, const struct stream_message *message);
/* Путь без нулей/повторов, начинается источником и заканчивается отправителем; forbidden не входит в него. */
int stream_path_validate(const struct stream_path *path, uint64_t source, uint64_t sender, uint64_t forbidden);
int stream_path_parse(const uint8_t *wire, size_t length, struct stream_path *path, size_t *used);
size_t stream_path_write(uint8_t *wire, const struct stream_path *path);
void stream_metric_write(uint8_t *wire, const struct stream_metric_snapshot *snapshot, int valid);
int stream_metric_read(const uint8_t *wire, struct stream_metric_snapshot *snapshot, int *valid);
int stream_end_sign(const struct stream_descriptor *descriptor, uint64_t last, const uint8_t key[32], uint8_t wire[STREAM_END_SIZE]);
int stream_end_verify(const struct stream_descriptor *descriptor, const uint8_t key[32], const uint8_t *wire, size_t length,
uint64_t *last);
#ifdef __cplusplus
}
#endif
#endif