Files
utun2/src/stream/stream.h
T

189 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* stream — публикация и приём трансляций CHAT-группы с подписью источника и восстановлением потерь через Reed–Solomon.
* Источник: publish_open → publish_write или publish_block → publish_finish → close.
* Приём: subscribe_open для событий байтового потока либо media_open → media_read для аудио/видео; затем close.
* Данные хранятся в общем кольце; у каждого локального читателя свой handle. Все вызовы — uasync-поток.
* Коды результатов — STREAM_* из stream_proto.h; STREAM_OK означает успех, STREAM_WAIT — данные ещё не готовы. */
/* Владение и callbacks:
* Последний reader close освобождает экземпляр; publisher close после FIN
* сохраняет кольцо для локальных readers, без FIN отсоединяет их. Отсоединённый handle также закрывает вызывающий.
* DATA/GAP/END/CLOSED/AVAILABLE callback отложен, data/описание заимствованы до его возврата.
* Close handles из callback допустим; остановка сервиса/группы и destroy ядра — вне callbacks.
* Источник и локальные подписки публикуются атомарно через опции собственного мембера. */
#ifndef STREAM_H
#define STREAM_H
#include "stream_buffer.h"
#include "stream_media_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; }; // n — всего RS-частей, r — проверочных; block_size — максимум части.
enum stream_event_type { STREAM_EVENT_DATA, STREAM_EVENT_GAP, STREAM_EVENT_END, STREAM_EVENT_CLOSED, STREAM_EVENT_AVAILABLE };
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; // DATA, GAP, END, CLOSED или готовность медиа AVAILABLE
uint64_t first_seq, last_seq; // DATA/GAP: включительный диапазон пачек; END: последняя пачка, UINT64_MAX при пустом потоке
const uint8_t *data; // DATA: восстановленные байты, заимствованы до возврата callback
size_t length; // размер data в байтах
int reason; // CLOSED: stream_close_reason
};
typedef void (*stream_event_fn)(struct UTUN_INSTANCE *inst, const struct stream_event *event, void *arg);
struct stream_info {
struct stream_descriptor descriptor; // источник, эпоха и параметры RS
struct stream_buffer_stats buffer; // текущее состояние общего кольца
unsigned owners; // число локальных handles общей сессии
int publisher; // 1 — сессия содержит локальный источник
int finished; // 1 — источник завершён; оставшиеся данные ещё могут читаться
};
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, score_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;
};
// ── Жизненный цикл сервиса ──
// Чат-сервис создаёт общий контекст до открытия handles; STREAM_OK или отрицательная ошибка.
int stream_service_init(struct UTUN_INSTANCE *inst);
// Остановить трансляции и отсоединить handles; вызывающий закрывает оставшиеся handles сам. Вне callbacks.
void stream_service_destroy(struct UTUN_INSTANCE *inst);
// Отсоединить трансляции группы при её удалении; handles остаются у владельцев до close.
void stream_cancel_group(struct UTUN_INSTANCE *inst, uint64_t group_id);
// ── События и каталог источников ──
// Единственный общий обработчик событий экземпляра, arg живёт до снятия; callback=NULL отключает доставку.
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);
/* Независимое наблюдение owner: начальный снимок и изменения через CHAT_EVT_STREAM_SOURCES.
* group_id=0 снимает только этого owner; owner=0 разрешён для выбранного канала frontend.
* Недоступный каталог имеет valid=0 и не означает завершение источников.
* Не создаёт подписку/буфер и не удерживает ACTIVE. Только uasync. */
int stream_watch_sources(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t owner);
// ── Согласование с активностью ядра и рацией ──
// Локальный owner удерживает эффективный ACTIVE; желаемый frontend-режим ядро восстанавливает после последнего close.
int stream_service_needs_activity(const struct UTUN_INSTANCE *inst);
// Только локальный viewer этой группы/эпохи подавляет рацию источника со включённым звуком.
int stream_radio_muted(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t source_id);
// ── Открытие источника и подписок ──
/* Открыть источник; *out заранее NULL. parameters=NULL использует STREAM_DEFAULT_N/R: 8 частей без избыточности.
* Максимальный размер части задаёт STREAM_RS_DEFAULT_SIZE; одна подпись защищает всю пачку внутри RS.
* Одна публикация этого узла в группе; новый запуск получает новый source_epoch. При успехе handle закрывает вызывающий. */
int stream_publish_open(struct UTUN_INSTANCE *inst, uint64_t group_id, const struct stream_parameters *parameters,
struct STREAM_HANDLE **out);
// Меняет подписанное состояние звука, не формат медиатреков. Только uasync; raw publisher начинает с 0.
int stream_publish_set_audio_enabled(struct STREAM_HANDLE *handle, int enabled);
/* Параметры берутся из подписанного профиля источника; дубликаты локальных владельцев делят один буфер.
* 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);
// ── Выбор поставщиков и состояние handles ──
// По умолчанию: подтверждение улучшения 2000 мс, удержание 5000 мс, гистерезис интегральной оценки 20 мс.
int stream_selection_configure(struct STREAM_HANDLE *handle, const struct stream_selection_settings *settings);
// Скопировать статистику выбора; для сессии без автоматического планировщика — нули. STREAM_OK/STREAM_ERR_STATE.
int stream_selection_get_info(const struct STREAM_HANDLE *handle, struct stream_selection_info *info);
// Освободить свой handle, в том числе после CLOSED/удаления группы; NULL допустим. После close указатель недействителен.
void stream_close(struct STREAM_HANDLE *handle);
// 1 — handle ещё связан с рабочей сессией; 0 — NULL или отсоединён. Не проверяет наличие свежих данных.
int stream_handle_active(const struct STREAM_HANDLE *handle);
// Причина отсоединения из stream_close_reason; для NULL — STREAM_CLOSE_STOPPED.
int stream_handle_reason(const struct STREAM_HANDLE *handle);
// Скопировать состояние общей сессии и кольца; STREAM_OK или STREAM_ERR_STATE.
int stream_get_info(const struct STREAM_HANDLE *handle, struct stream_info *info);
// ── Чтение аудио/видео ──
/* Медиаплеер использует тот же RS-buffer, но собственные позиции чтения.
* tracks=0 включает только общий индекс; VIDEO или VIDEO|AUDIO создаёт cursor этого handle.
* Все вызовы — uasync. Заимствованный packet действует до следующего read/close.
* LIVE DATA/GAP/END callbacks описывают сетевой прогресс; конец отложенного просмотра получает media_read. */
int stream_media_enable(struct STREAM_HANDLE *handle, unsigned tracks);
/* Атомарно открыть viewer выбранной эпохи с собственным cursor. CONFLICT означает смену источника.
* tracks=VIDEO или VIDEO|AUDIO; out заранее NULL, при отказе остаётся NULL. Закрытие через stream_close. */
int stream_media_open(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t source_id, uint64_t epoch,
unsigned tracks, struct STREAM_HANDLE **out);
/* Прочитать ближайшую медиазапись: want_video=1 разрешает видео, звук ограничен position_us+audio_ahead_us.
* STREAM_OK — packet заполнен, его kind также сообщает конец трека/потока; WAIT/MORE — повторить позже.
* GAP/EXPIRED требуют новой точки KEY через resync; отрицательный результат — ошибка. Буфер packet заимствован. */
int stream_media_read(struct STREAM_HANDLE *handle, int64_t position_us, int want_video, int64_t audio_ahead_us,
struct stream_media_packet *packet);
// Снимок курсора и доступных медиаданных; STREAM_OK или отрицательная ошибка.
int stream_media_get_status(struct STREAM_HANDLE *handle, struct stream_media_status *status);
// Сбросить только свой медиакурсор для поиска новой точки KEY; STREAM_OK или STREAM_ERR_STATE.
int stream_media_resync(struct STREAM_HANDLE *handle);
// ── Публикация байтового потока ──
// Максимальная порция одного write/block в байтах; 0 — неактивный handle или не источник.
size_t stream_publish_capacity(const struct STREAM_HANDLE *handle);
/* Скопировать length <= stream_publish_capacity(handle) байт в источник; accepted обязателен даже при ошибке.
* Входной буфер остаётся у вызывающего. STREAM_OK — принято; отрицательный результат — ошибка. */
int stream_publish_write(struct STREAM_HANDLE *handle, const uint8_t *data, size_t length, size_t *accepted);
/* Скопировать одну непустую порцию размером <= capacity как отдельную FEC-пачку; STREAM_OK или отрицательная ошибка.
* Входной буфер остаётся у вызывающего. Не смешивать с 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