test(media): cover voice duration bounds and early rejection
This commit is contained in:
1 parent
68a7695d81
commit
359a9f3dfc
2 files changed
+106
-20
No files matched your search
@@ -388,6 +388,9 @@ test_reality_config_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
test_dm_SOURCES = test_dm.c
|
||||
test_attachment_SOURCES = test_attachment.c
|
||||
test_attachment_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
if OS_LINUX
|
||||
test_attachment_LDFLAGS = -Wl,--wrap=opus_codec_encoder_create
|
||||
endif
|
||||
test_encrypted_file_SOURCES = test_encrypted_file.c
|
||||
test_encrypted_file_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
if OS_LINUX
|
||||
|
||||
+103
-20
@@ -6,8 +6,89 @@
|
||||
#include "../lib/opus_codec.h"
|
||||
#include "../src/media_async/attachment.h"
|
||||
#include "../src/media_async/voice_file.h"
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#define REQUIRE(value) do { if (!(value)) { fprintf(stderr, "attachment check failed line=%d\n", __LINE__); return 1; } } while (0)
|
||||
|
||||
/* Проверяем настоящий контейнер и число декодируемых кадров, включая отбрасывание хвоста. */
|
||||
static int check_voice_roundtrip(const int16_t* pcm, size_t count, int rate, int channels, int preset, uint32_t duration_ms) {
|
||||
struct attachment_info info = { .name = "voice.opus" };
|
||||
const char* path = "test_attachment_voice.opus";
|
||||
REQUIRE(!voice_file_encode(pcm, count, rate, channels, preset, path, &info));
|
||||
REQUIRE(info.duration_ms == duration_ms && strlen(info.waveform) == 100 && !attachment_validate(&info));
|
||||
FILE* file = fopen(path, "rb"); REQUIRE(file != NULL);
|
||||
uint8_t header[12]; REQUIRE(fread(header, 1, 12, file) == 12);
|
||||
uint32_t magic, saved_rate; uint16_t saved_channels, frame;
|
||||
memcpy(&magic, header, 4); memcpy(&saved_rate, header + 4, 4);
|
||||
memcpy(&saved_channels, header + 8, 2); memcpy(&frame, header + 10, 2);
|
||||
REQUIRE(magic == 0x5355504f && saved_rate == (uint32_t)rate && saved_channels == channels && frame == rate / 50);
|
||||
opus_codec_decoder_t* decoder = opus_codec_decoder_create(rate, channels); REQUIRE(decoder != NULL);
|
||||
uint8_t packet[4000]; int16_t decoded[1920]; unsigned frames = 0;
|
||||
for (;;) {
|
||||
uint16_t n; REQUIRE(fread(&n, 2, 1, file) == 1);
|
||||
if (!n) break;
|
||||
REQUIRE(n <= sizeof(packet) && fread(packet, 1, n, file) == n);
|
||||
REQUIRE(opus_codec_decode(decoder, packet, n, decoded, frame) == frame); frames++;
|
||||
}
|
||||
REQUIRE(frames == duration_ms / 20 && fgetc(file) == EOF);
|
||||
REQUIRE(!fclose(file) && !remove(path)); opus_codec_decoder_destroy(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int encoder_probe;
|
||||
static unsigned encoder_calls;
|
||||
opus_codec_encoder_t* __real_opus_codec_encoder_create(int rate, int channels);
|
||||
opus_codec_encoder_t* __wrap_opus_codec_encoder_create(int rate, int channels) {
|
||||
if (!encoder_probe) return __real_opus_codec_encoder_create(rate, channels);
|
||||
encoder_calls++;
|
||||
return NULL; /* Останавливаем только probe: PCM ещё не прочитан, файл/метаданные не изменены. */
|
||||
}
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
/* Виртуальный PCM позволяет проверить суточную границу без выделения физической памяти/суточного Opus. */
|
||||
static int check_voice_duration(void) {
|
||||
static const int rates[] = {8000, 12000, 16000, 24000, 48000};
|
||||
static const uint32_t durations[] = {0, 479, 480, 499, 500, 519, 86399980, 86400000, 86400019, 86400020, 86400980, 86401000};
|
||||
const char* path = "test_attachment_duration.opus";
|
||||
static const char marker[] = "existing voice output";
|
||||
size_t bytes = (size_t)48000 * 2 * 86401 * sizeof(int16_t);
|
||||
int16_t* pcm = mmap(NULL, bytes, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
REQUIRE(pcm != MAP_FAILED);
|
||||
FILE* file = fopen(path, "wb"); REQUIRE(file != NULL);
|
||||
REQUIRE(fwrite(marker, 1, sizeof(marker), file) == sizeof(marker) && !fclose(file));
|
||||
encoder_probe = 1;
|
||||
unsigned cases = 0, rejected = 0;
|
||||
debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG);
|
||||
for (size_t r = 0; r < sizeof(rates) / sizeof(rates[0]); r++) {
|
||||
for (int channels = 1; channels <= 2; channels++) {
|
||||
for (size_t d = 0; d < sizeof(durations) / sizeof(durations[0]); d++) {
|
||||
uint32_t duration = durations[d];
|
||||
size_t count = (size_t)rates[r] * channels * duration / 1000;
|
||||
int allowed = duration >= 500 && duration < 86400020;
|
||||
struct attachment_info info = { .kind = ATTACHMENT_FILE, .name = "unchanged.bin" };
|
||||
unsigned char before[sizeof(info)]; memcpy(before, &info, sizeof(info));
|
||||
encoder_calls = 0;
|
||||
REQUIRE(voice_file_encode(pcm, count, rates[r], channels, 0, path, &info) == -1);
|
||||
REQUIRE(encoder_calls == (unsigned)allowed && !memcmp(&info, before, sizeof(info)));
|
||||
file = fopen(path, "rb"); REQUIRE(file != NULL);
|
||||
char saved[sizeof(marker)]; REQUIRE(fread(saved, 1, sizeof(saved), file) == sizeof(saved));
|
||||
REQUIRE(!memcmp(saved, marker, sizeof(saved)) && fgetc(file) == EOF && !fclose(file));
|
||||
cases++; rejected += !allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
encoder_probe = 0;
|
||||
REQUIRE(!munmap(pcm, bytes) && !remove(path));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "voice duration: cases=%u rejected_before_encoder=%u file/metadata preserved", cases, rejected);
|
||||
debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_ERROR);
|
||||
REQUIRE(cases == 120 && rejected == 70);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
|
||||
struct attachment_info in = { .kind = ATTACHMENT_VOICE, .name = "голос.opus", .duration_ms = 1200 }, out;
|
||||
@@ -24,35 +105,37 @@ int main(void) {
|
||||
length = attachment_encode(&in, wire, sizeof(wire)); REQUIRE(length > 0 && !attachment_decode(wire, length, &out));
|
||||
REQUIRE(out.width == 640 && out.height == 480);
|
||||
in.width = 0; REQUIRE(attachment_validate(&in) < 0);
|
||||
REQUIRE(ATTACHMENT_DURATION_MAX_MS == 86400000);
|
||||
for (int kind = ATTACHMENT_VOICE; kind <= ATTACHMENT_VIDEO; kind++) {
|
||||
in = (struct attachment_info){ .kind = kind, .name = "duration.bin", .duration_ms = 86400000 };
|
||||
if (kind == ATTACHMENT_VOICE) memset(in.waveform, 'A', 100);
|
||||
else { in.width = 640; in.height = 480; }
|
||||
length = attachment_encode(&in, wire, sizeof(wire)); REQUIRE(length > 0 && !attachment_decode(wire, length, &out));
|
||||
in.duration_ms++; REQUIRE(attachment_validate(&in) < 0 && attachment_encode(&in, wire, sizeof(wire)) < 0);
|
||||
wire[6]++; REQUIRE(attachment_decode(wire, length, &out) < 0);
|
||||
}
|
||||
in = (struct attachment_info){ .kind = ATTACHMENT_FILE, .name = "../unsafe" }; REQUIRE(attachment_validate(&in) < 0);
|
||||
strcpy(in.name, "\xc0\xaf"); REQUIRE(attachment_validate(&in) < 0);
|
||||
memset(in.name, 'n', 255); in.name[255] = 0;
|
||||
REQUIRE(attachment_encode(&in, wire, 265) < 0); REQUIRE(attachment_encode(&in, wire, 266) == 266);
|
||||
int16_t* pcm = u_calloc(48001, sizeof(*pcm)); REQUIRE(pcm != NULL);
|
||||
for (unsigned i = 0; i < 48001; i++) pcm[i] = (int16_t)((i % 200) * 100 - 10000);
|
||||
in = (struct attachment_info){ .name = "voice.opus" };
|
||||
const char* path = "test_attachment_voice.opus";
|
||||
REQUIRE(!voice_file_encode(pcm, 48001, 48000, 1, 1, path, &in));
|
||||
REQUIRE(in.duration_ms == 1000 && strlen(in.waveform) == 100 && !attachment_validate(&in));
|
||||
FILE* file = fopen(path, "rb"); REQUIRE(file != NULL);
|
||||
uint8_t header[12]; REQUIRE(fread(header, 1, 12, file) == 12);
|
||||
uint32_t magic, rate; uint16_t channels, frame;
|
||||
memcpy(&magic, header, 4); memcpy(&rate, header + 4, 4); memcpy(&channels, header + 8, 2); memcpy(&frame, header + 10, 2);
|
||||
REQUIRE(magic == 0x5355504f && rate == 48000 && channels == 1 && frame == 960);
|
||||
opus_codec_decoder_t* decoder = opus_codec_decoder_create(48000, 1); REQUIRE(decoder != NULL);
|
||||
uint8_t packet[4000]; int16_t decoded[960]; unsigned frames = 0;
|
||||
for (;;) {
|
||||
uint16_t n; REQUIRE(fread(&n, 2, 1, file) == 1);
|
||||
if (!n) break;
|
||||
REQUIRE(n <= sizeof(packet) && fread(packet, 1, n, file) == n);
|
||||
REQUIRE(opus_codec_decode(decoder, packet, n, decoded, 960) == 960); frames++;
|
||||
int16_t* pcm = u_calloc(49920, sizeof(*pcm)); REQUIRE(pcm != NULL);
|
||||
for (unsigned i = 0; i < 49920; i++) pcm[i] = (int16_t)((i % 200) * 100 - 10000);
|
||||
REQUIRE(!check_voice_roundtrip(pcm, 48001, 48000, 1, 1, 1000));
|
||||
static const int rates[] = {8000, 12000, 16000, 24000, 48000};
|
||||
for (size_t r = 0; r < sizeof(rates) / sizeof(rates[0]); r++) {
|
||||
for (int channels = 1; channels <= 2; channels++) {
|
||||
size_t frame = (size_t)(rates[r] / 50) * channels;
|
||||
REQUIRE(!check_voice_roundtrip(pcm, frame * 26 - 1, rates[r], channels, (r + channels) % 3, 500));
|
||||
}
|
||||
}
|
||||
REQUIRE(frames == 50 && fgetc(file) == EOF);
|
||||
fclose(file); remove(path); opus_codec_decoder_destroy(decoder);
|
||||
const char* path = "test_attachment_voice.opus";
|
||||
REQUIRE(voice_file_encode(pcm, 23999, 48000, 1, 1, path, &in) < 0);
|
||||
REQUIRE(voice_file_encode(pcm, 48000, 48000, 1, 3, path, &in) < 0);
|
||||
REQUIRE(voice_file_encode(pcm, 48000, 44100, 1, 1, path, &in) < 0);
|
||||
u_free(pcm);
|
||||
#if defined(__linux__) && SIZE_MAX > UINT32_MAX
|
||||
REQUIRE(!check_voice_duration());
|
||||
#endif
|
||||
printf("test_attachment: strict metadata and shared Opus encode/decode PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user