diff --git a/tools/chatgui/src/stream_publish_engine.cpp b/tools/chatgui/src/stream_publish_engine.cpp index da71e4cc..f2d94b96 100644 --- a/tools/chatgui/src/stream_publish_engine.cpp +++ b/tools/chatgui/src/stream_publish_engine.cpp @@ -91,11 +91,6 @@ struct StreamPublishEngine::Impl { std::atomic audioDropped{0}, audioMissing{0}; std::atomic lastAudioAt{0}; AudioEventQueue audio; - std::condition_variable imageWake; - QVideoFrame imageInput; - uint64_t imageGeneration = 0; - int64_t imagePts = -1; - bool imagePending = false, imageStop = false, imageDrawCursor = false; uint64_t id = 0, group = 0, generation = 0, sourceGeneration = 0, confirmedGeneration = 0, frameGeneration = 0, audioSequence = 0; int64_t origin = 0, started = 0, framePts = -1, backendOrigin = -1, backendAnchor = -1; int64_t audioAnchor = -1; @@ -129,6 +124,12 @@ struct StreamPublishEngine::Impl { QList codecs; stream_publisher_status stats{}; #ifdef HAVE_STREAM_CAPTURE + // Очередь преобразования принадлежит capture backend; без Qt Multimedia нет ни QVideoFrame, ни её worker. + std::condition_variable imageWake; + QVideoFrame imageInput; + uint64_t imageGeneration = 0; + int64_t imagePts = -1; + bool imagePending = false, imageStop = false, imageDrawCursor = false; std::unique_ptr session; std::unique_ptr sink; std::unique_ptr camera; @@ -209,6 +210,7 @@ StreamPublishEngine::StreamPublishEngine(QObject* parent) : QObject(parent), m(s m->imageThread->start(); #else m->imageJoined = true; + DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop stream capture unavailable in this build; image worker disabled"); #endif } StreamPublishEngine::~StreamPublishEngine() { diff --git a/tools/chatgui/tests/test_stream_publish.cpp b/tools/chatgui/tests/test_stream_publish.cpp index 40aa13ec..4258eae7 100644 --- a/tools/chatgui/tests/test_stream_publish.cpp +++ b/tools/chatgui/tests/test_stream_publish.cpp @@ -195,6 +195,30 @@ private slots: sqlite3_finalize(statement); QVERIFY(group); QVERIFY(gui_stream_attach(inst)); uasync_mark_running(ua); connect(&poll, &QTimer::timeout, this, [this] { uasync_poll(ua, 0); }); poll.start(1); } +#ifndef HAVE_STREAM_CAPTURE + void captureUnavailableRefusesPublication() { + StreamPublishEngine engine; + QVERIFY(!StreamPublishEngine::supported()); QVERIFY(StreamPublishEngine::sources().isEmpty()); + QVERIFY(engine.codecs().isEmpty()); QVERIFY(!engine.previewFrame().isValid()); + StreamPublishWindow window(&engine, group); window.show(); + auto* source = window.findChild(QStringLiteral("streamVideoSource")); + QVERIFY(source); QCOMPARE(source->count(), 0); + QPushButton* start = nullptr; + for (auto* button : window.findChildren()) + if (button->text() == QCoreApplication::translate("ChatGui", "Start stream")) start = button; + QVERIFY(start); QVERIFY(!start->isEnabled()); + QSignalSpy errors(&engine, &StreamPublishEngine::errorOccurred); + QSignalSpy states(&engine, &StreamPublishEngine::stateChanged); + QTest::mouseClick(start, Qt::LeftButton); QCOMPARE(errors.count(), 0); + QVERIFY(!engine.start(group, StreamPublishConfig{})); + QCOMPARE(errors.count(), 1); QVERIFY(!errors.front().front().toString().isEmpty()); + QTest::qWait(120); QVERIFY(!engine.active()); QVERIFY(!engine.running()); QCOMPARE(engine.group(), uint64_t(0)); + QCOMPARE(states.count(), 0); QCOMPARE(engine.videoBitrate(), uint64_t(0)); QCOMPARE(engine.videoMaxQp(), -1); + AudioCaptureLease microphone; QVERIFY(microphone.acquire()); microphone.release(); + engine.stop(); engine.stop(); QVERIFY(!engine.active()); + DEBUG_INFO(DEBUG_CATEGORY_STREAM, "disabled capture verified: UI start disabled, API refused, actor and microphone unowned"); + } +#endif void amfDriverDiagnosticsRespectCategoryFilter() { #ifdef HAVE_FFMPEG video_ffmpeg_init();