You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

142 lines
6.9 KiB

// media_delivery.h — распространение медиа (аудио/видео стриминг)
#ifndef MEDIA_DELIVERY_H
#define MEDIA_DELIVERY_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "../lib/ll_queue.h"
#include "../lib/sqlite3.h"
struct UTUN_INSTANCE;
/* ── константы ── */
#define MEDIA_MAX_REPL_INFLIGHT 4
#define MEDIA_REPL_TIMEOUT_TB 20000 // начальный таймаут 2s (0.1ms)
#define MEDIA_REPL_TIMEOUT_MAX_TB 6000000 // макс 10 min
#define MEDIA_MAX_DOWNLOADS 10
#define MEDIA_QUERY_TIMEOUT_TB 20000 // таймаут QUERY 2s
#define MEDIA_HAVE_BLOCK_TIMEOUT_TB 20000 // таймаут HAVE_BLOCK_ACK 2s
#define MEDIA_RECONNECT_COOLDOWN_TB 360000000 // 1 час (0.1ms)
#define MEDIA_HELLO_TIMEOUT_TB 20000 // таймаут SUPER_HELLO 2s
/* ── структуры ── */
struct media_super_peer {
struct ll_entry ll; // индекс по peer_node_id (8 байт)
uint64_t peer_node_id;
uint64_t peer_last_recv_id; // что пир говорит он получил от нас (из SUPER_HELLO / SUPER_ACK)
uint32_t timeout_tb; // текущий таймаут (растёт при ошибках)
uint8_t inflight_count; // пакетов в полёте (макс 4)
uint8_t connected; // 1 = прямое подключение установлено
uint8_t hello_done; // 1 = SUPER_HELLO обмен завершён, можно реплицировать
void* timeout_timer; // таймер ретрансмита
void* connect_timer; // таймер переподключения (1 час при ошибке)
struct media_delivery_ctx* md; // обратная ссылка
};
/* per-file download tracking (source node admission control) */
struct md_file_load {
struct ll_entry ll; // индекс по media_id (16 байт)
uint8_t media_id[16];
int active_downloads; // текущее число исходящих стримов
uint64_t downloader_ids[10]; // кто сейчас качает (для редиректа)
};
/*
* block_availability: жизненный цикл записей и гарантия уникальности id
*
* Каждое событие создаёт новую запись с новым id (AUTOINCREMENT).
* Старая запись удаляется — id НЕ переиспользуется.
* Это критично для репликации: курсор super_sync.last_recv_id
* всегда монотонно растёт и не пропускает события.
*
* BLOCK_PROCESSING (subcmd 0x10):
* md_ba_insert(... , status=0) — чистый INSERT, новый id=X.
*
* HAVE_BLOCK (subcmd 0x06):
* md_ba_complete_block():
* 1. DELETE FROM block_availability WHERE block_uuid=? AND node_id=? AND status=0
* (удаляет processing-запись id=X, если была)
* 2. Если completed уже есть → no-op (возврат 0)
* 3. INSERT ... status=1 — новый id=Y.
*
* SUPER_REPL (subcmd 0x08):
* Принимающая сторона:
* - status=0: INSERT OR IGNORE (дубликаты при репликации — норма)
* - status=1: md_ba_complete_block() — та же логика что HAVE_BLOCK
*
* Итог: processing id=X удаляется, completed вставляется с новым id=Y>X.
* Репликация через md_ba_get_since(id > last_recv_id) всегда видит Y.
*/
struct media_served_node {
struct ll_entry ll; // индекс по node_id (8 байт)
uint64_t node_id;
uint64_t group_id;
int64_t joined_at; // timestamp регистрации
};
#define MD_MAX_RELAY_DOWNSTREAM 5
struct relay_downstream {
uint64_t node_id;
uint64_t group_id;
uint64_t sent_offset; // сколько байт уже отправлено этому пиру
struct queue_waiter_handle waiter;
};
struct relay_block_ctx {
struct ll_entry ll; // индекс по block_id (16 байт)
uint8_t block_id[16];
uint8_t media_id[16];
uint32_t chunk;
uint64_t file_offset; // всего байт получено от апстрима (размер .chunk_N)
char chunk_file[2048]; // путь к .chunk_N файлу для relay-чтения
struct relay_downstream downstream[MD_MAX_RELAY_DOWNSTREAM];
int downstream_count;
struct UTUN_INSTANCE* inst;
};
struct media_delivery_ctx {
uint64_t self_node_id;
int initialized;
uint8_t is_supernode; // из adm_tags в peers_*
uint8_t active_streams; // текущее число исходящих стримов (admission)
uint8_t max_downloads_per_file; // лимит параллельных загрузок с одного файла (source node), default 3
uint8_t stream_completed; // 1 = хотя бы один стрим завершился (для тестов)
uint8_t streams_started; // монотонный счётчик запущенных стримов (для тестов)
sqlite3* db; // = inst->topo_sqlite_db (кешируем для удобства)
struct ll_queue* served_nodes; // media_served_node — только суперузел
struct ll_queue* super_peers; // media_super_peer — только суперузел
struct ll_queue* downloads; // media_download — активные загрузки
struct ll_queue* relay_blocks; // relay_block_ctx — relay в процессе загрузки
struct ll_queue* file_loads; // md_file_load — per-file download count (source node)
struct UTUN_INSTANCE* inst;
};
int media_delivery_init(struct UTUN_INSTANCE* inst);
void media_delivery_destroy(struct UTUN_INSTANCE* inst);
int media_delivery_bind(struct UTUN_INSTANCE* inst);
void media_delivery_set_supernode(struct UTUN_INSTANCE* inst, int is_supernode);
void media_delivery_stream_done(struct UTUN_INSTANCE* inst);
/* relay block context helpers (used by media_download.c) */
struct relay_block_ctx* md_relay_find(struct media_delivery_ctx* md, const uint8_t* block_id);
struct relay_block_ctx* md_relay_add(struct media_delivery_ctx* md,
const uint8_t* block_id, const uint8_t* media_id, uint32_t chunk);
void md_relay_remove(struct media_delivery_ctx* md, const uint8_t* block_id);
/* file_load helpers (per-file source download tracking) */
struct md_file_load* md_file_load_find(struct media_delivery_ctx* md, const uint8_t* media_id);
int md_file_load_inc(struct media_delivery_ctx* md, const uint8_t* media_id, uint64_t node_id);
void md_file_load_dec(struct media_delivery_ctx* md, const uint8_t* media_id, uint64_t node_id);
#ifdef __cplusplus
}
#endif
#endif