Specify video publication, remux and completion contracts

This commit is contained in:
evgeny committed 2026-10-21 23:58:55 +02:00
1 parent 8d9704036f
commit c4bf292085
1 file changed
+34 -18
+34 -18
View File
@@ -1,4 +1,10 @@
// video.h — пробинг и транскодирование видео (FFmpeg, опционально)
/* Подготовка локальных видеофайлов через FFmpeg: метаданные, JPEG и MP4 H.264/AAC.
* probe/get_info/thumbnail выполняют синхронное чтение; запускать вне GUI/uasync при медленном I/O.
* transcode_start передаёт работу media_async; публикация готового файла и done — в потоке uasync.
* Исходник должен оставаться неизменным до done. Назначение — новое имя или обычный файл, без ссылок.
* Один исходный и выходной файл, в том числе через hardlink, запрещены. Ошибка/отмена сохраняет старый dst.
* Подготовка пишет закрытый проверенный временный файл рядом с dst, затем заменяет конечное имя.
* В процессе допускается одна задача до её completion; thumbnail/probe независимы от этого лимита. */
#ifndef VIDEO_H
#define VIDEO_H
@@ -27,60 +33,70 @@ int video_codec_log_level(int level);
#define VIDEO_TARGET_MAX_HEIGHT 720
#define VIDEO_TARGET_MAX_BITRATE 2000000 // 2 Mbit (только видео)
#define VIDEO_TARGET_AUDIO_BITRATE 128000 // AAC 128 kbit
#define VIDEO_PATH_MAX 1024 // Размер буфера пути с завершающим NUL; длинные пути отклоняются.
/* ── информация о видеофайле ── */
struct video_info {
int is_video; // 1 = открыт и есть видеопоток
int width;
int height;
int64_t bitrate; // bitrate видеопотока (или контейнера)
int64_t duration_ms;
int has_audio;
int width; // Ширина выбранного видеопотока в пикселях.
int height; // Высота выбранного видеопотока в пикселях.
int64_t bitrate; // Средний bitrate выбранного видеопотока, бит/с; 0 — неизвестен.
int64_t duration_ms; // Длительность контейнера/видеопотока, мс; 0 — неизвестна.
int has_audio; // Есть выбранный аудиопоток; отсутствие декодера не означает отсутствие аудио.
int is_yuv420p; // Видео имеет 8-битный YUV420P; иначе требуется перекодирование.
int64_t audio_bitrate; // Средний bitrate выбранного аудиопотока; 0 — неизвестен/нет аудио.
char audio_codec[32]; // Имя аудиокодека, например "aac"; пусто без аудио.
char codec[32]; // напр. "h264", "hevc", "mpeg4"
char container[32]; // короткое имя контейнера (ifmt->name)
};
/*
* Быстрый пробинг (читает только заголовок). Вызывается из любого потока
* (в т.ч. из GUI-потока Qt) — thread-safe, не блокирует надолго.
* Возвращает 1 если файл содержит ffmpeg-совместимый видеопоток, иначе 0.
* Синхронный пробинг: FFmpeg может читать/анализировать пакеты, ограничения времени нет.
* Возвращает 1 при наличии видеопотока, кроме attached picture; иначе 0, включая сборку без FFmpeg.
*/
int video_probe(const char* path);
/* Полное чтение метаданных. Заполняет info. Возвращает 0 при успехе. */
/* Синхронно прочитать метаданные выбранных потоков. path/out обязательны; 0 — успех, -1 — ошибка.
* out обнуляется до работы, в том числе без FFmpeg; при ошибке содержимое не использовать. */
int video_get_info(const char* path, struct video_info* out);
/*
* Решает, нужен ли транскод: 0 = уже подходит (слать как есть),
* 1 = нужен транскод. Вызывается из любого потока.
* 0 — достаточно remux MOV/MP4/Matroska в MP4; 1 — нужно перекодирование выбранных потоков.
* Проверяет известные bitrate, размеры, H.264/YUV420P и AAC при наличии аудио.
* NULL, неизвестный профиль или сборка без FFmpeg — 1. Выход start всегда формируется как MP4.
*/
int video_needs_transcode(const struct video_info* info);
/*
* Запуск транскодирования. Вызывается из uasync-потока.
* Создаёт отдельный pthread, который либо перекодирует src->dst в целевой
* профиль, либо (если транскод не нужен) копирует src->dst как есть.
* owner/ua/src/dst/done обязательны; owner обслуживается только в этом uasync-потоке.
* Worker либо перекодирует видео/аудио, либо выполняет remux без изменения сжатых кадров.
* Прогресс обновляется атомарно, читается через video_transcode_get_progress().
* При возврате 0 done(arg, err) вызывается ровно один раз в uasync-потоке,
* в том числе при отмене owner. Callback может выполниться внутри start.
* При возврате -1 callback не вызывается, arg остаётся у вызывающего.
* done: err=0 — dst опубликован, info заимствован только на время callback; при ошибке info=NULL.
* err=-1 — подготовка/запуск/публикация не удалась, MEDIA_ASYNC_CANCELLED (-2) — отмена owner.
* destroy owner ждёт worker, затем отменяет публикацию и вызывает done; мгновенного прерывания кодека нет.
*/
struct media_async;
int video_transcode_start(struct media_async* owner, struct UASYNC* ua,
const char* src, const char* dst,
void (*done)(void* arg, int err, const struct video_info* info), void* arg);
/* Прогресс активного транскода в промилле 0..1000, -1 если транскода нет. */
/* Глобальный прогресс 0..1000; 1000 — временный файл подготовлен, публикация ещё может ожидаться.
* -1 — активной задачи нет. Читать можно из любого потока; active/progress не являются единым снимком. */
int video_transcode_get_progress(void);
/* 1 = транскод выполняется прямо сейчас. */
/* 1 — задача занята работой/ожиданием completion; 0 — можно запускать следующую. */
int video_transcode_is_active(void);
/*
* Извлечь превью: декодирует первый видеокадр, масштабирует до max_w по ширине
* и сохраняет JPEG в jpg_out. Вызывается из любого потока (thread-safe).
* Возвращает 0 при успехе.
* и сохраняет JPEG в jpg_out. Синхронно; параллельные вызовы используют разные назначения.
* max_w<=0 означает 320; допустимый явный предел >=2, размеры округляются до чётных.
* Правила файлов/длины путей — как у start; 0 — опубликован JPEG, -1 — ошибка/нет FFmpeg.
*/
int video_extract_thumbnail(const char* src, const char* jpg_out, int max_w);