/* Стриминг CHAT-сервиса: локальные handles владеют общим экземпляром трансляции. * Все API — uasync-поток. Последний reader close освобождает экземпляр; publisher close * останавливает свою трансляцию и отсоединяет её readers. Отсоединённый handle всё равно закрывает вызывающий. * DATA/GAP/END/CLOSED callback отложен, data/описание заимствованы до его возврата. * Close handles из callback допустим; остановка сервиса/группы и destroy ядра — вне callbacks. * Источник и локальные подписки публикуются атомарно через опции собственного мембера. */ #ifndef STREAM_H #define STREAM_H #include "stream_buffer.h" #ifdef __cplusplus extern "C" { #endif struct UTUN_INSTANCE; struct STREAM_HANDLE; struct stream_service; struct stream_parameters { uint8_t n, r; uint16_t block_size; }; enum stream_event_type { STREAM_EVENT_DATA, STREAM_EVENT_GAP, STREAM_EVENT_END, STREAM_EVENT_CLOSED }; enum stream_close_reason { STREAM_CLOSE_STOPPED, STREAM_CLOSE_GROUP, STREAM_CLOSE_SERVICE, STREAM_CLOSE_SOURCE_CHANGED, STREAM_CLOSE_MEMBER, STREAM_CLOSE_FAILED }; struct stream_event { struct stream_descriptor descriptor; enum stream_event_type type; uint64_t first_seq, last_seq; const uint8_t *data; size_t length; int reason; }; typedef void (*stream_event_fn)(struct UTUN_INSTANCE *inst, const struct stream_event *event, void *arg); struct stream_info { struct stream_descriptor descriptor; struct stream_buffer_stats buffer; unsigned owners; int publisher; int finished; }; struct stream_candidate { uint64_t node_id; uint8_t client_type; uint8_t source; }; struct stream_selection_settings { uint32_t migration_confirm_ms, minimum_hold_ms, rtt_hysteresis_ms; }; struct stream_selection_info { uint64_t probes_started, cycles, migrations, failures, restarts; unsigned candidates, probes_active, subflows, overlaps, providers; int automatic, protected_by_fec; }; int stream_service_init(struct UTUN_INSTANCE *inst); void stream_service_destroy(struct UTUN_INSTANCE *inst); void stream_cancel_group(struct UTUN_INSTANCE *inst, uint64_t group_id); void stream_set_event_callback(struct UTUN_INSTANCE *inst, stream_event_fn callback, void *arg); /* Проверенные online-публикации: JSON с id(decimal string), epoch(hex string), name, self. * Только uasync; *out заранее NULL, результат освобождается u_free(). WAIT — группа недоступна. */ int stream_sources_json(struct UTUN_INSTANCE *inst, uint64_t group_id, char **out); /* Один выбранный канал frontend: начальный снимок и изменения через CHAT_EVT_STREAM_SOURCES. * group_id=0 снимает наблюдение. Не создаёт подписку/буфер и не удерживает ACTIVE. Только uasync. */ int stream_watch_sources(struct UTUN_INSTANCE *inst, uint64_t group_id); /* Локальный owner удерживает эффективный ACTIVE; желаемый frontend-режим ядро восстанавливает после последнего close. */ int stream_service_needs_activity(const struct UTUN_INSTANCE *inst); /* out заранее NULL. parameters=NULL — 6+2 и полный подписанный блок 1300 байт. * Одна публикация этого узла в группе; новый запуск получает новый source_epoch. */ int stream_publish_open(struct UTUN_INSTANCE *inst, uint64_t group_id, const struct stream_parameters *parameters, struct STREAM_HANDLE **out); /* Параметры берутся из подписанного профиля источника; дубликаты локальных владельцев делят один буфер. * WAIT — источник ещё не объявил/не подтвердил трансляцию. Offline не создаёт ложного онлайн-кандидата. */ int stream_subscribe_open(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t source_id, struct STREAM_HANDLE **out); /* Ручные выдачи для диагностики/явного управления; режим общий для всех owners экземпляра и не смешивается с automatic. */ int stream_subscribe_manual_open(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t source_id, struct STREAM_HANDLE **out); /* По умолчанию: подтверждение улучшения 2000 мс, удержание 5000 мс, гистерезис RTT 20 мс. */ int stream_selection_configure(struct STREAM_HANDLE *handle, const struct stream_selection_settings *settings); int stream_selection_get_info(const struct STREAM_HANDLE *handle, struct stream_selection_info *info); void stream_close(struct STREAM_HANDLE *handle); int stream_handle_active(const struct STREAM_HANDLE *handle); int stream_handle_reason(const struct STREAM_HANDLE *handle); int stream_get_info(const struct STREAM_HANDLE *handle, struct stream_info *info); size_t stream_publish_capacity(const struct STREAM_HANDLE *handle); int stream_publish_write(struct STREAM_HANDLE *handle, const uint8_t *data, size_t length, size_t *accepted); /* Публикует одну непустую FEC-пачку, без склейки/таймера. Не смешивать с publish_write в одном запуске. */ int stream_publish_block(struct STREAM_HANDLE *handle, const uint8_t *data, size_t length); /* Сбросить остаток, снять статус публикации; DATA перед END. Буфер живёт до close. */ int stream_publish_finish(struct STREAM_HANDLE *handle); /* Только подходящие online-подписчики этой группы/эпохи; мобильные relay исключены, источник допускается. * out заранее NULL; массив освобождается u_free(). Наличие кандидата не гарантирует прямой транспорт. */ int stream_get_candidates(const struct STREAM_HANDLE *handle, struct stream_candidate **out, size_t *count); #ifdef __cplusplus } #endif #endif