Reproduce stale stream captions without restarting playback

This commit is contained in:
evgeny committed 2026-10-28 00:58:59 +01:00
1 parent debd3b45ef
commit 9d2ddcc496
1 file changed
+76
+76
View File
@@ -566,6 +566,82 @@ private slots:
#endif
}
void gridRenamesPreservePlayback_data() {
QTest::addColumn<QString>("name");
QTest::newRow("ordinary") << QString("Renamed source");
QTest::newRow("unicode") << QString::fromUtf8("Новое имя 👩‍💻 «Источник»");
QTest::newRow("empty-name") << QString();
}
void gridRenamesPreservePlayback() {
#if defined(TEST_NULL_AUDIO) && defined(HAVE_FFMPEG) && defined(HAVE_STREAM_CAPTURE)
QFETCH(QString, name);
if (publisher) { QCOMPARE(stream_publish_finish(publisher), STREAM_OK); stream_close(publisher); publisher = nullptr; }
QCOMPARE(stream_publish_open(inst, group, nullptr, &publisher), STREAM_OK);
stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); epoch = info.descriptor.source_epoch;
QVERIFY(SoundManager::instance()->init());
StreamGridWindow grid(group); grid.show();
const uint64_t own = inst->node_id ^ uint64_t(1);
QString ownName = "Own original", remoteName = "Remote original";
auto snapshot = [&](bool valid = true, uint64_t eventGroup = 0) {
QJsonArray sources{
QJsonObject{{"id", QString::number(own)}, {"epoch", QString::number(epoch, 16)}, {"name", ownName}, {"self", 1}},
QJsonObject{{"id", QString::number(inst->node_id)}, {"epoch", QString::number(epoch, 16)}, {"name", remoteName}, {"self", 0}}};
QByteArray bytes(9, '\0'); stream_write_be(reinterpret_cast<uint8_t*>(bytes.data()), eventGroup ? eventGroup : group, 8);
bytes[8] = valid; bytes += QJsonDocument(sources).toJson(QJsonDocument::Compact);
grid.onSources(reinterpret_cast<const uint8_t*>(bytes.constData()), bytes.size());
};
snapshot();
QPointer<QWidget> local = grid.findChild<QWidget*>(QStringLiteral("streamPreview_%1").arg(own)); QVERIFY(local);
QPointer<StreamPlayerWindow> remote = grid.findChild<StreamPlayerWindow*>(); QVERIFY(remote);
QPointer<QLabel> localTitle, remoteTitle;
for (auto* label : local->findChildren<QLabel*>()) if (label->text() == ownName) localTitle = label;
for (auto* label : remote->findChildren<QLabel*>()) if (label->text() == remoteName) remoteTitle = label;
QVERIFY(localTitle); QVERIFY(remoteTitle);
QPointer<QVideoWidget> preview = local->findChild<QVideoWidget*>(); QVERIFY(preview);
QVideoFrame frame(QImage(16, 16, QImage::Format_RGB32)); frame.setStartTime(123000);
grid.setLocalPreview(group, QVariant::fromValue(frame));
QPointer<VideoRenderWidget> video = remote->findChild<VideoRenderWidget*>(); QVERIFY(video);
auto* volume = remote->findChild<QSlider*>(); QVERIFY(volume); volume->setValue(37);
QTRY_VERIFY(stream_get_info(publisher, &info) == STREAM_OK && info.owners == 2);
QTest::mouseClick(video, Qt::LeftButton); QTRY_COMPARE(grid.focusedSource(), inst->node_id);
QVERIFY(grid.isFullScreen()); QVERIFY(local->isHidden());
QSignalSpy titles(remote, &QWidget::windowTitleChanged);
ownName = name.isEmpty() ? QString() : name + " / local";
remoteName = name.isEmpty() ? QString() : name + " / remote";
const QString expectedLocal = name.isEmpty() ? QString::number(own) : ownName;
const QString expectedRemote = name.isEmpty() ? QString::number(inst->node_id) : remoteName;
snapshot(false); snapshot(true, group ^ (uint64_t(1) << 63));
QCOMPARE(localTitle->text(), QString("Own original")); QCOMPARE(remoteTitle->text(), QString("Remote original"));
snapshot();
QCOMPARE(localTitle->text(), expectedLocal); QCOMPARE(localTitle->toolTip(), expectedLocal);
QCOMPARE(remoteTitle->text(), expectedRemote); QCOMPARE(remoteTitle->toolTip(), expectedRemote);
QCOMPARE(remote->windowTitle(), QString("Live stream: %1").arg(expectedRemote)); QCOMPARE(titles.count(), 1);
snapshot(); QCOMPARE(titles.count(), 1);
QVERIFY(local); QVERIFY(remote); QVERIFY(preview); QVERIFY(video);
QCOMPARE(grid.findChild<StreamPlayerWindow*>(), remote.data()); QCOMPARE(remote->findChild<VideoRenderWidget*>(), video.data());
QCOMPARE(preview->videoSink()->videoFrame().startTime(), qint64(123000)); QCOMPARE(volume->value(), 37);
QCOMPARE(grid.focusedSource(), inst->node_id); QVERIFY(grid.isFullScreen()); QVERIFY(local->isHidden());
QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); QCOMPARE(info.owners, 2u); QCOMPARE(info.descriptor.source_epoch, epoch);
// Неудачный reader должен повторно открыться с последним именем, без сброса соседнего preview.
gui_stream_detach(inst); QTRY_VERIFY(remote->failed());
remoteName = QString::fromUtf8("После переподключения 🧑‍💻"); snapshot();
QCOMPARE(remoteTitle->text(), remoteName); QVERIFY(gui_stream_attach(inst));
QTRY_VERIFY_WITH_TIMEOUT(remote.isNull(), 2500);
auto* retried = grid.findChild<StreamPlayerWindow*>(); QVERIFY(retried);
QCOMPARE(retried->windowTitle(), QString("Live stream: %1").arg(remoteName));
bool currentCaption = false;
for (auto* label : retried->findChildren<QLabel*>())
currentCaption |= label->text() == remoteName && label->toolTip() == remoteName;
QVERIFY(currentCaption); QVERIFY(local); QVERIFY(preview);
QCOMPARE(preview->videoSink()->videoFrame().startTime(), qint64(123000));
QTRY_VERIFY(stream_get_info(publisher, &info) == STREAM_OK && info.owners == 2);
grid.close(); QTRY_VERIFY(stream_get_info(publisher, &info) == STREAM_OK && info.owners == 1);
#else
QSKIP("Rename fixture requires null audio, FFmpeg and Qt Multimedia");
#endif
}
void boundedBatchesAndNotifications() {
QCOMPARE(stream_publish_finish(publisher), STREAM_OK); stream_close(publisher); publisher = nullptr;
QCOMPARE(stream_publish_open(inst, group, nullptr, &publisher), STREAM_OK);