189 lines
14 KiB
C
189 lines
14 KiB
C
/* 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
|