Support JPEG and PNG attachment metadata and preparation
This commit is contained in:
1 parent
78ea57d183
commit
e3b09bd0b7
3 files changed
+24
-12
No files matched your search
@@ -38,14 +38,17 @@ int attachment_validate(const struct attachment_info* info) {
|
||||
if (wave) goto invalid;
|
||||
if (info->kind == ATTACHMENT_FILE) {
|
||||
if (info->duration_ms || info->width || info->height) goto invalid;
|
||||
} else if (info->kind == ATTACHMENT_VIDEO) {
|
||||
} else if (info->kind == ATTACHMENT_VIDEO || info->kind == ATTACHMENT_JPEG || info->kind == ATTACHMENT_PNG) {
|
||||
if (!info->width || !info->height || info->width > 16384 || info->height > 16384) goto invalid;
|
||||
if (!info->duration_ms || info->duration_ms > ATTACHMENT_DURATION_MAX_MS) goto invalid;
|
||||
if (info->kind == ATTACHMENT_VIDEO) {
|
||||
if (!info->duration_ms || info->duration_ms > ATTACHMENT_DURATION_MAX_MS) goto invalid;
|
||||
} else if (info->duration_ms) goto invalid;
|
||||
} else goto invalid;
|
||||
}
|
||||
return 0;
|
||||
invalid:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: invalid metadata kind=%d", info ? (int)info->kind : 0);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: invalid metadata kind=%d dimensions=%ux%u duration=%u",
|
||||
info ? (int)info->kind : 0, info ? info->width : 0, info ? info->height : 0, info ? info->duration_ms : 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ const char* attachment_content_type(enum attachment_kind kind) {
|
||||
switch (kind) {
|
||||
case ATTACHMENT_VOICE: return "audio/opus";
|
||||
case ATTACHMENT_VIDEO: return "video/mp4";
|
||||
case ATTACHMENT_JPEG: return "image/jpeg";
|
||||
case ATTACHMENT_PNG: return "image/png";
|
||||
default: return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* attachment — общие метаданные file/voice/video, без хранения файла, адресата и транспорта.
|
||||
/* attachment — общие метаданные file/voice/video/JPEG/PNG, без хранения файла, адресата и транспорта.
|
||||
* validate/decode возвращают 0 / -1, encode — число записанных байтов / -1.
|
||||
* Формат: kind:1, name_len:2, duration_ms:4, width:2, height:2, UTF-8 basename, waveform:100 только для voice.
|
||||
* Числа big-endian; имя — корректный UTF-8 basename длиной 1..255 байт, без '/'/'\\', ASCII C0/DEL и '.'/'..'.
|
||||
@@ -12,15 +12,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum attachment_kind { ATTACHMENT_FILE = 1, ATTACHMENT_VOICE, ATTACHMENT_VIDEO };
|
||||
enum attachment_kind { ATTACHMENT_FILE = 1, ATTACHMENT_VOICE, ATTACHMENT_VIDEO, ATTACHMENT_JPEG, ATTACHMENT_PNG };
|
||||
#define ATTACHMENT_WAVE_SIZE 100
|
||||
#define ATTACHMENT_WIRE_MAX (11 + 255 + ATTACHMENT_WAVE_SIZE)
|
||||
#define ATTACHMENT_DURATION_MAX_MS 86400000u /* Общий максимум длительности voice/video: 24ч. */
|
||||
struct attachment_info {
|
||||
enum attachment_kind kind; /* Только FILE/VOICE/VIDEO. */
|
||||
enum attachment_kind kind; /* FILE/VOICE/VIDEO/JPEG/PNG; формат изображения не зависит от имени. */
|
||||
char name[256]; /* UTF-8 basename с завершающим NUL. */
|
||||
uint32_t duration_ms; /* VOICE/VIDEO: 1..ATTACHMENT_DURATION_MAX_MS; FILE: 0. */
|
||||
uint16_t width, height; /* VIDEO: 1..16384; FILE/VOICE: 0. */
|
||||
uint32_t duration_ms; /* VOICE/VIDEO: 1..ATTACHMENT_DURATION_MAX_MS; остальные: 0. */
|
||||
uint16_t width, height; /* VIDEO/JPEG/PNG: 1..16384, изображения с учётом EXIF; FILE/VOICE: 0. */
|
||||
char waveform[ATTACHMENT_WAVE_SIZE + 1]; /* 100 уровней из A-Za-z0-9+/; только voice. */
|
||||
};
|
||||
/* Проверить все поля и их согласованность с kind; info не меняется, NULL даёт -1. */
|
||||
@@ -31,7 +31,7 @@ int attachment_encode(const struct attachment_info* info, uint8_t* out, size_t c
|
||||
/* Разобрать ровно length байт и проверить метаданные; data/info обязательны. 0/-1.
|
||||
* При ошибке info может быть изменена: результат используется только при возврате 0. */
|
||||
int attachment_decode(const uint8_t* data, size_t length, struct attachment_info* info);
|
||||
/* Заимствованная постоянная строка: VOICE=audio/opus, VIDEO=video/mp4, иначе application/octet-stream. Не валидатор kind. */
|
||||
/* Постоянный MIME по kind: audio/opus, video/mp4, image/jpeg, image/png либо application/octet-stream. Не валидатор kind. */
|
||||
const char* attachment_content_type(enum attachment_kind kind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -182,14 +182,21 @@ void attachment_send_trampoline(void* arg) {
|
||||
for (unsigned i = 0; i < 16; i++) snprintf(hex + 2 * i, 3, "%02x", uuid[i]);
|
||||
memcpy(req->id, uuid, sizeof(req->id));
|
||||
const char* base = req->inst->config->global.db_path;
|
||||
const char* extension = "file";
|
||||
switch (req->info.kind) {
|
||||
case ATTACHMENT_VOICE: extension = "opus"; break;
|
||||
case ATTACHMENT_VIDEO: extension = "mp4"; break;
|
||||
case ATTACHMENT_JPEG: extension = "jpg"; break;
|
||||
case ATTACHMENT_PNG: extension = "png"; break;
|
||||
default: break;
|
||||
}
|
||||
int n = snprintf(req->output, sizeof(req->output), "%s/%s/%s/%s.%s", base[0] ? base : ".",
|
||||
req->encrypted && !req->is_dm ? "encrypted-prepared" : "media",
|
||||
req->is_dm ? "pm-prepared" : req->target, hex, req->info.kind == ATTACHMENT_VOICE ? "opus" : "mp4");
|
||||
req->is_dm ? "pm-prepared" : req->target, hex, extension);
|
||||
if (n < 0 || (size_t)n >= sizeof(req->output)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment: output path too long"); req->output[0] = 0; attachment_publish(req, -1); return;
|
||||
}
|
||||
if (!req->info.name[0]) snprintf(req->info.name, sizeof(req->info.name), "%s.%s", hex,
|
||||
req->info.kind == ATTACHMENT_VOICE ? "opus" : "mp4");
|
||||
if (!req->info.name[0]) snprintf(req->info.name, sizeof(req->info.name), "%s.%s", hex, extension);
|
||||
if (req->is_dm) {
|
||||
if (dm_media_file_hold(req->inst, req->target, req->dm_generation, req->output)) { attachment_publish(req, -1); return; }
|
||||
req->dm_paths_held |= 1;
|
||||
|
||||
Reference in new issue
Block a user