126 lines
8.9 KiB
Plaintext
126 lines
8.9 KiB
Plaintext
/* Стриминг CHAT-сервиса: локальные handles владеют общим экземпляром трансляции.
|
||
* Все API — uasync-поток. Последний 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; }; /* block_size — максимум RS-части. */
|
||
|
||
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;
|
||
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, 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;
|
||
};
|
||
|
||
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);
|
||
/* Независимое наблюдение 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 — 6+2, часть до 1300 байт и одна подпись пачки внутри RS.
|
||
* Одна публикация этого узла в группе; новый запуск получает новый source_epoch. */
|
||
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);
|
||
/* По умолчанию: подтверждение улучшения 2000 мс, удержание 5000 мс, гистерезис интегральной оценки 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);
|
||
|
||
/* Медиаплеер использует тот же 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. */
|
||
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);
|
||
int stream_media_get_status(struct STREAM_HANDLE *handle, struct stream_media_status *status);
|
||
int stream_media_resync(struct STREAM_HANDLE *handle);
|
||
|
||
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
|