diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 97f9bd52..5ff17ebd 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -510,6 +510,10 @@ target_link_libraries(test_stream_view PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun_vo if(FFMPEG_FOUND) target_compile_definitions(test_stream_view PRIVATE HAVE_FFMPEG) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_definitions(test_stream_view PRIVATE TEST_NULL_AUDIO) + target_link_options(test_stream_view PRIVATE "-Wl,--wrap=ma_context_init" "-Wl,--wrap=ma_device_init") +endif() add_test(NAME test_stream_view COMMAND test_stream_view) set_tests_properties(test_stream_view PROPERTIES TIMEOUT 30 ENVIRONMENT "QT_QPA_PLATFORM=offscreen") diff --git a/tools/chatgui/tests/test_stream_view.cpp b/tools/chatgui/tests/test_stream_view.cpp index 69bb780c..6ee621ce 100644 --- a/tools/chatgui/tests/test_stream_view.cpp +++ b/tools/chatgui/tests/test_stream_view.cpp @@ -1,10 +1,14 @@ /* Настоящее ядро/actor, подписанный медиаблок и H.264 decoder: каталог до подписки, эпоха, показ и teardown. */ #include "../src/streamplayer_window.h" +#include "../src/sound_manager.h" #include "../transport/stream_bridge.h" #include #include #include #include +#include +#include +#include extern "C" { #include "utun_instance.h" @@ -22,6 +26,27 @@ extern "C" { } static QList snapshots; +#ifdef TEST_NULL_AUDIO +static std::atomic originalAudioCallback{nullptr}; +static std::atomic nonzeroSamples{0}; +extern "C" ma_result __real_ma_context_init(const ma_backend*, ma_uint32, const ma_context_config*, ma_context*); +extern "C" ma_result __real_ma_device_init(ma_context*, const ma_device_config*, ma_device*); +extern "C" ma_result __wrap_ma_context_init(const ma_backend*, ma_uint32, const ma_context_config*, ma_context* context) { + ma_backend backend = ma_backend_null; return __real_ma_context_init(&backend, 1, nullptr, context); +} +static void observeAudio(ma_device* device, void* out, const void* in, ma_uint32 frames) { + originalAudioCallback.load()(device, out, in, frames); + const auto* pcm = static_cast(out); + for (unsigned i = 0; i < frames; i++) if (pcm[i]) nonzeroSamples.fetch_add(1); +} +extern "C" ma_result __wrap_ma_device_init(ma_context* context, const ma_device_config* config, ma_device* device) { + ma_device_config copy = *config; + if (copy.deviceType == ma_device_type_playback && copy.playback.format == ma_format_s16 && copy.playback.channels == 1) { + originalAudioCallback = copy.dataCallback; copy.dataCallback = observeAudio; + } + return __real_ma_device_init(context, ©, device); +} +#endif static void sourceEvent(UTUN_INSTANCE*, int type, const uint8_t* data, int size) { if (type == CHAT_EVT_STREAM_SOURCES) snapshots.push_back(QByteArray(reinterpret_cast(data), size)); } @@ -130,10 +155,24 @@ private slots: AVPacket* packet = av_packet_alloc(); QVERIFY(packet); QCOMPARE(avcodec_receive_packet(context, packet), 0); QByteArray video(reinterpret_cast(packet->data), packet->size); av_packet_free(&packet); av_frame_free(&frame); avcodec_free_context(&context); + QByteArray audio; +#ifdef TEST_NULL_AUDIO + QVERIFY(SoundManager::instance()->init()); + auto* opus = opus_codec_encoder_create(48000, 1); QVERIFY(opus); + int16_t pcm[960]; for (unsigned i = 0; i < 960; i++) pcm[i] = static_cast(6000 * std::sin(i * 0.05)); + uint8_t encoded[1275]; int length = opus_codec_encode(opus, pcm, 960, encoded, sizeof(encoded)); QVERIFY(length > 0); + audio = QByteArray(reinterpret_cast(encoded), length); opus_codec_encoder_destroy(opus); + nonzeroSamples = 0; +#endif StreamPlayerWindow window(group, inst->node_id, epoch, "Source"); QSignalSpy shown(&window, &StreamPlayerWindow::framePresented); window.show(); - QTimer sender; connect(&sender, &QTimer::timeout, this, [this, video] { publish(video); }); sender.start(100); + QTimer sender; connect(&sender, &QTimer::timeout, this, [this, video, audio] { publish(video, audio); }); sender.start(100); QTRY_VERIFY_WITH_TIMEOUT(shown.count() > 0, 5000); +#ifdef TEST_NULL_AUDIO + QTRY_VERIFY_WITH_TIMEOUT(nonzeroSamples.load() > 0, 5000); + auto* volume = window.findChild(); QVERIFY(volume); volume->setValue(0); + QTest::qWait(50); uint64_t played = nonzeroSamples.load(); QTest::qWait(100); QCOMPARE(nonzeroSamples.load(), played); +#endif sender.stop(); window.close(); QTest::qWait(20); stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); QCOMPARE(info.owners, 1u); #else @@ -166,6 +205,7 @@ private slots: void cleanupTestCase() { poll.stop(); gui_stream_detach(inst); stream_close(publisher); uasync_mark_stopped(ua); + SoundManager::instance()->shutdown(); utun_instance_destroy(inst); uasync_destroy(ua, 0); QCOMPARE(u_get_allocated_count(), allocations); }