Browse Source

Verify stereo media format and AGC channel balance

master
evgeny 6 days ago
parent
commit
c251de4f77
  1. 31
      tests/test_audio_compressor.c
  2. 29
      tests/test_stream_media.c

31
tests/test_audio_compressor.c

@ -244,40 +244,49 @@ static void test_streaming_matches_recording(void) {
audio_compressor_destroy(recording); audio_compressor_destroy(streaming);
}
/* Тракт трансляции: усиление текущего mono-кадра перед Opus, live bypass и смена предела gain. */
/* Тракт трансляции: общий gain стереопары перед Opus, live bypass и смена предела gain. */
static void test_live_opus_gain(void) {
struct audio_compressor* ac = audio_compressor_create();
opus_codec_encoder_t* encoder = opus_codec_encoder_create(48000, 1);
opus_codec_decoder_t* decoder = opus_codec_decoder_create(48000, 1);
opus_codec_encoder_t* encoder = opus_codec_encoder_create(48000, 2);
opus_codec_decoder_t* decoder = opus_codec_decoder_create(48000, 2);
TEST(ac && encoder && decoder, "live audio codecs created");
if (!ac || !encoder || !decoder) goto done;
audio_compressor_config_t cfg = {.sample_rate = 48000, .channels = 1, .block_duration_ms = 20,
audio_compressor_config_t cfg = {.sample_rate = 48000, .channels = 2, .block_duration_ms = 20,
.lookback_ms = 200, .lookahead_ms = 0, .max_gain_db = 6, .rise_rate_per_sec = 6,
.release_rate_per_sec = 30, .target_level = 0.25f};
TEST(audio_compressor_configure(ac, &cfg) == 0, "live radio compressor configuration");
TEST(opus_codec_encoder_application_set(encoder, OPUS_CODEC_APP_AUDIO) == 0, "live Opus audio mode");
TEST(opus_codec_encoder_bitrate_set(encoder, 64000) == 0, "live Opus bitrate");
int16_t input[960], compressed[960], decoded[960]; uint8_t packet[1275];
TEST(opus_codec_encoder_bitrate_set(encoder, 128000) == 0, "live Opus bitrate");
int16_t input[1920], compressed[1920], decoded[1920]; uint8_t packet[1275];
size_t allocations = u_get_allocated_count();
for (int frame = 0; frame < 30; frame++) {
for (unsigned i = 0; i < 960; i++) input[i] = (int16_t)(500 * sin((frame * 960 + i) * 2 * M_PI * 440 / 48000));
TEST(audio_compressor_process_frame(ac, input, compressed, 960) == 0, "live current frame compressed");
for (unsigned i = 0; i < 960; i++) {
input[2 * i] = (int16_t)(500 * sin((frame * 960 + i) * 2 * M_PI * 440 / 48000));
input[2 * i + 1] = (int16_t)(125 * sin((frame * 960 + i) * 2 * M_PI * 1100 / 48000));
}
TEST(audio_compressor_process_frame(ac, input, compressed, 1920) == 0, "live current stereo frame compressed");
int length = opus_codec_encode(encoder, compressed, 960, packet, sizeof(packet));
TEST(length > 0, "live compressed frame encoded");
if (length <= 0) goto done;
TEST(opus_codec_decode(decoder, packet, length, decoded, 960) == 960, "live decoded packet has 20ms of PCM");
}
double input_energy = 0, output_energy = 0;
for (unsigned i = 0; i < 960; i++) { input_energy += (double)input[i] * input[i]; output_energy += (double)decoded[i] * decoded[i]; }
for (unsigned i = 0; i < 1920; i++) { input_energy += (double)input[i] * input[i]; output_energy += (double)decoded[i] * decoded[i]; }
TEST(output_energy > input_energy * 2.5 && output_energy < input_energy * 5, "gain survives real Opus roundtrip");
double left_energy = 0, right_energy = 0;
for (unsigned i = 0; i < 960; i++) {
left_energy += (double)decoded[2 * i] * decoded[2 * i];
right_energy += (double)decoded[2 * i + 1] * decoded[2 * i + 1];
}
TEST(left_energy > right_energy * 8 && left_energy < right_energy * 25, "stereo balance survives compressor and Opus");
TEST(u_get_allocated_count() == allocations, "live audio does not accumulate compressor buffers");
audio_compressor_set_enabled(ac, 0);
memcpy(compressed, input, sizeof(input));
TEST(audio_compressor_process_frame(ac, compressed, compressed, 960) == 0, "live bypass in place");
TEST(audio_compressor_process_frame(ac, compressed, compressed, 1920) == 0, "live bypass in place");
TEST(memcmp(input, compressed, sizeof(input)) == 0, "disabled compressor preserves current PCM exactly");
cfg.max_gain_db = 20;
TEST(audio_compressor_configure(ac, &cfg) == 0, "live gain limit changed"); audio_compressor_set_enabled(ac, 1);
for (unsigned frame = 0; frame < 50; frame++) TEST(audio_compressor_process_frame(ac, input, compressed, 960) == 0, "live new gain frame");
for (unsigned frame = 0; frame < 50; frame++) TEST(audio_compressor_process_frame(ac, input, compressed, 1920) == 0, "live new gain frame");
TEST(audio_compressor_gain_db(ac) > 19.9f && audio_compressor_gain_db(ac) <= 20.01f, "live new gain limit applied");
TEST(audio_compressor_output_size(ac) == 0, "live gain update introduces no output backlog");
done:

29
tests/test_stream_media.c

@ -177,13 +177,40 @@ static void test_revisions(void) {
stream_media_receiver_destroy(r);
}
static void stereo_frame(void *arg, const struct stream_media_format *format,
const struct stream_media_video *video, const struct stream_media_audio *audio) {
CHECK(!video && audio && audio->samples == 960 && format->audio_codec == STREAM_MEDIA_OPUS_STEREO);
(*(unsigned *)arg)++;
}
/* Стерео обозначается в wire явно; 960 — число кадров на канал, трек не меняется внутри эпохи. */
static void test_stereo(void) {
struct stream_media_block b = {.format = {1280, 720, 0, STREAM_MEDIA_H264, STREAM_MEDIA_OPUS_STEREO, 1}, .audio_count = 1};
b.audio[0] = (struct stream_media_audio){1000000, 3, 960, (const uint8_t *)"ops"};
uint8_t wire[7080]; size_t length = 0;
CHECK(stream_media_encode(&b, wire, sizeof(wire), &length) == STREAM_OK);
struct stream_media_block parsed;
CHECK(stream_media_parse(wire, length, &parsed) == STREAM_OK && parsed.format.audio_codec == STREAM_MEDIA_OPUS_STEREO);
unsigned received = 0; struct stream_media_receiver *receiver = NULL;
CHECK(stream_media_receiver_create(&receiver, stereo_frame, &received) == STREAM_OK);
CHECK(stream_media_receiver_feed(receiver, wire, length) == STREAM_OK && received == 1);
b.format.audio_codec = STREAM_MEDIA_OPUS; b.format.revision++; b.audio[0].pts_us += 20000;
CHECK(stream_media_encode(&b, wire, sizeof(wire), &length) == STREAM_OK);
CHECK(stream_media_receiver_feed(receiver, wire, length) == STREAM_ERR_CONFLICT && received == 1);
b.format.audio_codec = STREAM_MEDIA_OPUS_STEREO; b.audio[0].samples = 1920;
CHECK(stream_media_encode(&b, wire, sizeof(wire), &length) == STREAM_ERR_FORMAT);
b.audio[0].samples = 960; b.format.audio_codec = 3;
CHECK(stream_media_encode(&b, wire, sizeof(wire), &length) == STREAM_ERR_FORMAT);
stream_media_receiver_destroy(receiver);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
/* Усечения проверяются массово; ожидаемые отказы не загромождают диагностический лог. */
debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_DISABLED);
size_t before = u_get_allocated_count();
for (unsigned i = 0; i < sizeof(expected_video); i++) expected_video[i] = (uint8_t)(i * 31 + 7);
test_media(); test_batches(); test_revisions();
test_media(); test_batches(); test_revisions(); test_stereo();
CHECK(u_get_allocated_count() == before);
debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_INFO);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "PASS stream_media: protocol, FEC, frame recovery, boundaries, no leaks");

Loading…
Cancel
Save