diff --git a/src/media_async/attachment_send.h b/src/media_async/attachment_send.h index b07fd826..9f94e0f3 100644 --- a/src/media_async/attachment_send.h +++ b/src/media_async/attachment_send.h @@ -1,10 +1,15 @@ -/* attachment_send — подготовить голос/видео и отправить в зафиксированный канал или DM. Вызывать trampoline - * через uasync_post. Request и PCM/compressor переходят во владение задачи; - * pcm_release освобождает pcm_owner, NULL означает PCM из u_malloc. +/* attachment_send — подготовить вложение и передать его публикации в зафиксированный канал или DM. + * Trampoline вызывается в uasync (обычно через uasync_post), забирает req и PCM/compressor, включая отказ запуска. + * Если сам uasync_post не принят, request и его ресурсы остаются у вызывающего. + * pcm_release освобождает pcm_owner, NULL означает PCM из u_malloc; release может быть в worker + * или в uasync при отказе/отмене без work. Нельзя использовать GUI-объекты, требующие освобождения в UI-потоке. * Qt/Android используют общий результат подготовки, доставка остаётся в chat/dm. - * req выделяется u_calloc; caller заполняет target/is_dm, info и source либо PCM. inst должен жить до done. + * req выделяется u_calloc; caller заполняет target/is_dm, info и source либо PCM/compressor, строки NUL-терминированы. + * inst/config/media_async/uasync живут до завершения задачи. После вызова req нельзя читать или освобождать. + * Исходник не изменять во время подготовки; temporary_source разрешает удалить только собственный временный source. * Подготовка выполняется в media_async worker, публикация и attachment-события — в uasync. - * output/id/error заполняет задача; temporary_source разрешает удалить принадлежащий приложению source. */ + * Attachment-события 0/1/2 означают начало/готовность/ошибку подготовки, не подтверждение доставки сообщения. + * Подготовленный output затем забирает chat/dm; собственные пути и PCM освобождаются также при отмене. */ #ifndef ATTACHMENT_SEND_H #define ATTACHMENT_SEND_H #include "attachment.h" @@ -18,22 +23,23 @@ struct attachment_send_req { char target[64]; /* Десятичный channel_id либо conv_id без префикса dm:, зафиксированный до подготовки. */ int is_dm; int encrypted; /* Отдельный случайный ключ группового вложения с ручной выдачей. */ - char content_type[32]; /* Необязательный исходный MIME (например image/png). */ - uint64_t dm_generation; /* Захватывается до подготовки и сохраняется во всех её стадиях. */ - unsigned dm_paths_held; + char content_type[32]; /* Исходный MIME только для encrypted channel; пусто → MIME по kind. */ + uint64_t dm_generation; /* Поколение беседы до подготовки; 0 → захват в trampoline. Удалённое поколение не публикуется. */ + unsigned dm_paths_held; /* Внутренние holds output/source; caller оставляет 0. */ uint8_t reply_to[32]; /* Оригинал канального ответа, зафиксированный до подготовки. */ - uint64_t request_id; /* CHAT_EVT_MSG_SUBMITTED после публикации либо ошибки. */ - struct attachment_info info; - uint8_t id[16]; - char source[1024], output[1024]; - int transcode, temporary_source; + uint64_t request_id; /* Только канал: CHAT_EVT_MSG_SUBMITTED после публикации либо ошибки. Для DM — 0. */ + struct attachment_info info; /* kind обязателен; пустое name заменяется UUID.opus/mp4; для copy нужны готовые метаданные. */ + uint8_t id[16]; /* Внутренний UUID подготовки для attachment-событий; не UUID конечного media_index/DM. */ + char source[1024], output[1024]; /* source задаёт caller; output — внутреннее случайное назначение задачи. */ + int transcode, temporary_source; /* transcode только VIDEO: отдельный FFmpeg worker; иначе файл копируется. */ const int16_t* pcm; size_t pcm_count; /* Для voice: int16, 48 кГц моно, число отсчётов. */ - void* pcm_owner; - void (*pcm_release)(void* owner); - struct audio_compressor* compressor; - int preset, error; + void* pcm_owner; /* Аргумент собственного pcm_release. */ + void (*pcm_release)(void* owner); /* NULL → u_free(pcm); задача вызывает не более одного раза. */ + struct audio_compressor* compressor; /* Альтернатива PCM: задача flush/читает output/destroy, внешние обращения прекращены. */ + int preset, error; /* preset: 0..2 для voice; error заполняет worker. */ }; +/* arg — собственный struct attachment_send_req; завершается событиями chat/dm и освобождением req. NULL — no-op. */ void attachment_send_trampoline(void* arg); #ifdef __cplusplus } diff --git a/src/media_delivery/media_delivery.h b/src/media_delivery/media_delivery.h index 732b40ed..571e61d5 100644 --- a/src/media_delivery/media_delivery.h +++ b/src/media_delivery/media_delivery.h @@ -27,7 +27,7 @@ struct UTUN_INSTANCE; #define MEDIA_REPL_POLL_TB 300000 // периодическая сверка 30s, включая потерю CHANGED #define MEDIA_REPL_WATCHDOG_TB 2000 // контроль дедлайна/очереди/CM каждые 200ms #define MEDIA_QUERY_TIMEOUT_TB 20000 // таймаут QUERY 2s -#define MEDIA_HAVE_BLOCK_TIMEOUT_TB 20000 // таймаут HAVE_BLOCK_ACK 2s +#define MEDIA_HAVE_BLOCK_TIMEOUT_TB 20000 // объявлен 2s; текущий HAVE_BLOCK не ждёт ACK и не ставит этот таймер #define MEDIA_DL_STALL_TIMEOUT_TB 20000 // таймаут простоя загрузки 2s (0.1ms) #define MEDIA_DL_MAX_ATTEMPTS 3 // попыток на блок до ошибки diff --git a/src/media_delivery/media_delivery_proto.h b/src/media_delivery/media_delivery_proto.h index cf1cd1f4..6c621cf4 100644 --- a/src/media_delivery/media_delivery_proto.h +++ b/src/media_delivery/media_delivery_proto.h @@ -1,6 +1,9 @@ /* media_delivery_proto — wire-структуры и подкоманды доставки блоков медиа CHAT-группы. * Описывает запросы держателей, чанки, подтверждения наличия и репликацию между суперузлами. - * Это формат payload сервиса router, без группового маршрутизационного заголовка; обработчики — media_delivery/download. */ + * Это формат payload сервиса router, без группового маршрутизационного заголовка; обработчики — media_delivery/download. + * Числа SYNC кодируются little endian; остальные packed-поля — native byte order текущего формата. + * SERVE_REG/LEAVE меняют только вспомогательный served_nodes; текущий загрузчик их не посылает. + * SERVE_ACK сейчас не отправляется, входящие SERVE_ACK/HAVE_BLOCK_ACK игнорируются; ожидания ACK и его retry нет. */ #ifndef MEDIA_DELIVERY_PROTO_H #define MEDIA_DELIVERY_PROTO_H @@ -13,9 +16,9 @@ extern "C" { /* ── подкоманды (первый байт данных после etcp_router) ── */ enum { - MEDIA_SUBCMD_SERVE_REG = 0x01, // req-узел→суперузел: обслуживай меня - MEDIA_SUBCMD_SERVE_ACK = 0x02, // суперузел→req-узел: подтверждение - MEDIA_SUBCMD_SERVE_LEAVE = 0x03, // req-узел→суперузел: отключение + MEDIA_SUBCMD_SERVE_REG = 0x01, // вспомогательный учёт узла в served_nodes + MEDIA_SUBCMD_SERVE_ACK = 0x02, // объявлен, но не отправляется; приём игнорируется + MEDIA_SUBCMD_SERVE_LEAVE = 0x03, // удалить вспомогательную запись served_nodes MEDIA_SUBCMD_QUERY = 0x04, // req-узел→суперузел: кто имеет блоки? MEDIA_SUBCMD_QUERY_RESP = 0x05, // суперузел→req-узел: список узлов MEDIA_SUBCMD_HAVE_BLOCK = 0x06, // узел→суперузел: у меня есть блок @@ -34,7 +37,7 @@ enum { /* ── ответные статусы ── */ -#define MEDIA_ERR_NOT_SUPERNODE 0xFE +#define MEDIA_ERR_NOT_SUPERNODE 0xFE /* Объявлен, в текущих ответах не используется. */ /* Один QUERY — одна полная пачка результатов, включая блоки без держателей. * Группу и end-to-end отправителя задаёт router. Пределы общие для обеих сторон. */