Verify decoded video content, AAC signal timing and FFmpeg-free stubs

This commit is contained in:
evgeny committed 2026-10-22 00:08:22 +02:00
1 parent 9163025912
commit 9f1825dbf8
3 files changed
+64 -4

No files matched your search

+3
View File
@@ -10,6 +10,7 @@ EXTRA_DIST = fixtures/reality_openssl_hello.hex data/silero_speech.pcm data/sile
# Быстрые проверки (до 100мс).
check_PROGRAMS = \
test_video \
test_video_no_ffmpeg \
test_video_plane \
test_route_tx_stats \
test_call_video_rate \
@@ -786,6 +787,8 @@ test_call_video_LDADD = $(LIBUTUN) $(COMMON_LIBS)
test_video_SOURCES = test_video.c
test_video_LDADD = $(LIBUTUN) $(COMMON_LIBS)
test_video_no_ffmpeg_SOURCES = test_video_no_ffmpeg.c
test_video_no_ffmpeg_LDADD = $(COMMON_LIBS)
if HAVE_FFMPEG
if OS_LINUX
test_video_LDFLAGS = -Wl,--wrap=fdopen -Wl,--wrap=fwrite -Wl,--wrap=fflush -Wl,--wrap=fclose -Wl,--wrap=rename \
+34 -4
View File
@@ -134,6 +134,8 @@ static void fixture_packets(AVFormatContext* mux, AVCodecContext* enc, AVStream*
int error;
while ((error = avcodec_receive_packet(enc, packet)) == 0) {
av_packet_rescale_ts(packet, enc->time_base, stream->time_base);
if (enc->codec_type == AVMEDIA_TYPE_VIDEO && packet->duration <= 0)
packet->duration = av_rescale_q(1, enc->time_base, stream->time_base);
packet->stream_index = stream->index;
assert(av_interleaved_write_frame(mux, packet) >= 0);
}
@@ -161,7 +163,14 @@ static void fixture(const char* path, const char* format, enum AVCodecID video,
codec = avcodec_find_encoder(audio); assert(codec);
aenc = avcodec_alloc_context3(codec); assert(aenc);
aenc->sample_rate = 32000; aenc->time_base = (AVRational){1, 32000};
aenc->sample_fmt = codec->sample_fmts[0]; aenc->bit_rate = 64000; aenc->thread_count = 1;
#if LIBAVCODEC_VERSION_MAJOR >= 61
const void* formats;
assert(avcodec_get_supported_config(aenc, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, &formats, NULL) >= 0 && formats);
aenc->sample_fmt = ((const enum AVSampleFormat*)formats)[0];
#else
aenc->sample_fmt = codec->sample_fmts[0];
#endif
aenc->bit_rate = 64000; aenc->thread_count = 1;
av_channel_layout_default(&aenc->ch_layout, 1);
if (mux->oformat->flags & AVFMT_GLOBALHEADER) aenc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
assert(avcodec_open2(aenc, codec, NULL) >= 0);
@@ -192,6 +201,14 @@ static void fixture(const char* path, const char* format, enum AVCodecID video,
frame->nb_samples = block; frame->pts = sample + audio_offset_ms * aenc->sample_rate / 1000;
assert(av_frame_get_buffer(frame, 0) >= 0);
assert(av_samples_set_silence(frame->extended_data, 0, block, 1, aenc->sample_fmt) >= 0);
enum AVSampleFormat packed = av_get_packed_sample_fmt(aenc->sample_fmt);
for (int i = 0; i < block; i++) {
float value = (sample + i) % 64 < 32 ? 0.25f : -0.25f;
if (packed == AV_SAMPLE_FMT_FLT) ((float*)frame->data[0])[i] = value;
else if (packed == AV_SAMPLE_FMT_S16) ((int16_t*)frame->data[0])[i] = (int16_t)(value * 32767);
else if (packed == AV_SAMPLE_FMT_S32) ((int32_t*)frame->data[0])[i] = (int32_t)(value * 2147483647.0);
else assert(!"unsupported fixture sample format");
}
assert(avcodec_send_frame(aenc, frame) >= 0); fixture_packets(mux, aenc, astream);
av_frame_free(&frame);
}
@@ -256,6 +273,7 @@ static void decode_result(const char* path, unsigned expected_frames, int expect
av_packet_unref(packet);
while ((error = avcodec_receive_frame(dec, frame)) >= 0) {
assert(frame->width > 0 && frame->height > 0);
if (expected_frames == 6) assert(abs(frame->data[0][0] - (40 + (int)frames * 20)) <= 6);
if (last != AV_NOPTS_VALUE && frame->best_effort_timestamp != AV_NOPTS_VALUE) assert(frame->best_effort_timestamp > last);
last = frame->best_effort_timestamp; frames++;
}
@@ -274,7 +292,7 @@ static void decode_audio(const char* path, int minimum_samples, int offset_ms) {
AVCodecContext* decoder = avcodec_alloc_context3(avcodec_find_decoder(stream->codecpar->codec_id)); assert(decoder);
assert(avcodec_parameters_to_context(decoder, stream->codecpar) >= 0 && avcodec_open2(decoder, decoder->codec, NULL) >= 0);
AVPacket* packet = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); assert(packet && frame);
int samples = 0, eof = 0; int64_t first_us = AV_NOPTS_VALUE;
int samples = 0, eof = 0; int64_t first_us = AV_NOPTS_VALUE, signal_us = AV_NOPTS_VALUE;
while (!eof) {
int error = av_read_frame(format, packet);
if (error == AVERROR_EOF) eof = 1;
@@ -282,14 +300,26 @@ static void decode_audio(const char* path, int minimum_samples, int offset_ms) {
assert(avcodec_send_packet(decoder, eof ? NULL : packet) >= 0); av_packet_unref(packet);
while ((error = avcodec_receive_frame(decoder, frame)) >= 0) {
if (first_us == AV_NOPTS_VALUE) first_us = av_rescale_q(frame->best_effort_timestamp, stream->time_base, AV_TIME_BASE_Q);
assert(frame->format == AV_SAMPLE_FMT_FLTP);
if (signal_us == AV_NOPTS_VALUE) {
for (int sample = 0; sample < frame->nb_samples; sample++) {
float value = ((const float*)frame->extended_data[0])[sample];
if (value > 0.05f || value < -0.05f) {
signal_us = av_rescale_q(frame->best_effort_timestamp, stream->time_base, AV_TIME_BASE_Q) +
av_rescale(sample, 1000000, decoder->sample_rate);
break;
}
}
}
samples += frame->nb_samples;
}
assert(error == AVERROR_EOF || error == AVERROR(EAGAIN));
}
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "test_video decoded audio samples=%d first_us=%lld expected_offset_ms=%d",
samples, (long long)first_us, offset_ms);
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "test_video decoded audio samples=%d first_us=%lld signal_us=%lld expected_offset_ms=%d",
samples, (long long)first_us, (long long)signal_us, offset_ms);
assert(samples >= minimum_samples && samples <= minimum_samples + 2048);
assert(first_us != AV_NOPTS_VALUE && llabs(first_us - (int64_t)offset_ms * 1000) <= 33000);
assert(signal_us != AV_NOPTS_VALUE && llabs(signal_us - (int64_t)offset_ms * 1000) <= 10000);
av_packet_free(&packet); av_frame_free(&frame); avcodec_free_context(&decoder); avformat_close_input(&format);
}
+27
View File
@@ -0,0 +1,27 @@
/* Компилировать сам модуль без FFmpeg независимо от конфигурации основной сборки. */
#undef HAVE_FFMPEG
#include "../src/video/video.c"
#include <assert.h>
static unsigned callbacks;
static void unexpected_done(void* arg, int error, const struct video_info* info) {
(void)arg; (void)error; (void)info; callbacks++;
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
struct video_info actual, zero = {0}; memset(&actual, 0x5a, sizeof(actual));
video_ffmpeg_init();
assert(video_probe("unused") == 0);
assert(video_get_info("unused", &actual) == -1 && !memcmp(&actual, &zero, sizeof(actual)));
assert(video_get_info(NULL, NULL) == -1 && video_needs_transcode(NULL) == 1);
assert(video_transcode_start(NULL, NULL, "src", "dst", unexpected_done, NULL) == -1 && !callbacks);
assert(video_extract_thumbnail("src", "dst", 320) == -1);
assert(video_transcode_get_progress() == -1 && !video_transcode_is_active());
video_codec_probe_begin(); video_codec_probe_begin();
assert(video_codec_log_level(DEBUG_LEVEL_ERROR) == DEBUG_LEVEL_DEBUG);
video_codec_probe_end(); assert(video_codec_log_level(DEBUG_LEVEL_WARN) == DEBUG_LEVEL_DEBUG);
video_codec_probe_end(); assert(video_codec_log_level(DEBUG_LEVEL_ERROR) == DEBUG_LEVEL_ERROR);
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "PASS video build without FFmpeg");
return 0;
}