Verify shared stream mixing and avoid retrying terminal closures
This commit is contained in:
1 parent
4a44149c00
commit
b3116173ca
2 files changed
+57
-3
No files matched your search
@@ -242,8 +242,8 @@ void StreamPlayerWindow::decode() {
|
||||
if (request && !gui_stream_resync(p.id)) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop playback resync refused");
|
||||
deadline = nowUs() + 10000000; emit statusChanged(text("Waiting for a key frame…"));
|
||||
};
|
||||
auto fail = [&](const QString& reason) {
|
||||
p.failed = p.complete = true;
|
||||
auto fail = [&](const QString& reason, bool retry = true) {
|
||||
p.failed = retry; p.complete = true;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop viewer failed id=%llu: %s", (unsigned long long)p.id, qPrintable(reason));
|
||||
emit statusChanged(reason);
|
||||
};
|
||||
@@ -389,7 +389,7 @@ void StreamPlayerWindow::decode() {
|
||||
: record.reason == STREAM_CLOSE_MEMBER ? "The source is no longer a channel member"
|
||||
: record.reason == STREAM_CLOSE_SERVICE ? "Chat service is stopped"
|
||||
: record.reason == STREAM_CLOSE_GROUP ? "The channel is closed" : "Could not continue the stream";
|
||||
fail(text(reason)); break;
|
||||
fail(text(reason), record.reason == STREAM_CLOSE_FAILED); break;
|
||||
}
|
||||
if (record.kind == StreamRecordKind::End) {
|
||||
std::lock_guard<std::mutex> guard(p.lock); p.ended = true; p.holdAudio = false;
|
||||
|
||||
@@ -116,6 +116,8 @@ static std::atomic<uint64_t> silenceGaps{0};
|
||||
static unsigned silentRun = 0; // Только callback; счётчик промежутков >=1мс между тестовыми тонами.
|
||||
static std::atomic<uint64_t> leftEnergy{0}, rightEnergy{0};
|
||||
static std::atomic<int64_t> playbackDelayUs{0};
|
||||
static std::atomic<bool> inspectMixedOutput{false};
|
||||
static std::atomic<float> mixedMean{0}, mixedPeak{0};
|
||||
extern "C" int64_t __real_audio_stream_frame_time(ma_device*, unsigned int, int*);
|
||||
extern "C" int64_t __wrap_audio_stream_frame_time(ma_device* device, unsigned int frames, int* valid) {
|
||||
if (!playbackDelayUs) return __real_audio_stream_frame_time(device, frames, valid);
|
||||
@@ -140,6 +142,11 @@ static void observeAudio(ma_device* device, void* out, const void* in, ma_uint32
|
||||
else if (++silentRun == 48) silenceGaps.fetch_add(1);
|
||||
}
|
||||
leftEnergy.fetch_add(left); rightEnergy.fetch_add(right); nonzeroSamples.fetch_add(nonzero);
|
||||
if (inspectMixedOutput) {
|
||||
float mean = 0, peak = 0;
|
||||
for (unsigned i = 0; i < frames * 2; ++i) { mean += pcm[i]; peak = std::max(peak, std::abs(pcm[i])); }
|
||||
mixedMean = mean / (frames * 2); mixedPeak = peak;
|
||||
}
|
||||
}
|
||||
extern "C" ma_result __wrap_ma_device_init(ma_context* context, const ma_device_config* config, ma_device* device) {
|
||||
ma_device_config copy = *config;
|
||||
@@ -349,6 +356,46 @@ private slots:
|
||||
stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK); QCOMPARE(info.owners, 1u);
|
||||
}
|
||||
|
||||
void sharedOutputMixesWithoutRenormalizing() {
|
||||
#ifdef TEST_NULL_AUDIO
|
||||
auto* sound = SoundManager::instance(); QVERIFY(sound->init());
|
||||
std::atomic<unsigned> firstFrames{0}, secondFrames{0};
|
||||
struct Cleanup {
|
||||
SoundManager* sound;
|
||||
~Cleanup() {
|
||||
sound->detachStream(90001); sound->detachStream(90002); sound->detachStream(90003);
|
||||
inspectMixedOutput = false;
|
||||
}
|
||||
} cleanup{sound};
|
||||
inspectMixedOutput = true;
|
||||
QVERIFY(sound->attachStream(90001, [&](float* out, unsigned frames, int64_t, uint64_t) {
|
||||
for (unsigned i = 0; i < frames * 2; ++i) out[i] += 0.125f;
|
||||
firstFrames += frames;
|
||||
}));
|
||||
QTRY_VERIFY(std::abs(mixedMean.load() - 0.125f) < 0.001f);
|
||||
QVERIFY(sound->attachStream(90002, [&](float* out, unsigned frames, int64_t, uint64_t) {
|
||||
for (unsigned i = 0; i < frames * 2; ++i) out[i] += 0.25f;
|
||||
secondFrames += frames;
|
||||
}));
|
||||
QTRY_VERIFY(std::abs(mixedMean.load() - 0.375f) < 0.001f);
|
||||
QVERIFY(sound->attachStream(90003, [](float*, unsigned, int64_t, uint64_t) {}));
|
||||
QTest::qWait(30); QVERIFY(std::abs(mixedMean.load() - 0.375f) < 0.001f);
|
||||
sound->detachStream(90001);
|
||||
unsigned stopped = firstFrames, continuing = secondFrames;
|
||||
QTRY_VERIFY(std::abs(mixedMean.load() - 0.25f) < 0.001f);
|
||||
QTRY_VERIFY(secondFrames.load() > continuing); QCOMPARE(firstFrames.load(), stopped);
|
||||
sound->detachStream(90003);
|
||||
QVERIFY(sound->attachStream(90003, [](float* out, unsigned frames, int64_t, uint64_t) {
|
||||
for (unsigned i = 0; i < frames * 2; ++i) out[i] += 0.9f;
|
||||
}));
|
||||
QTRY_VERIFY(mixedMean.load() > 0.95f); QVERIFY(mixedPeak.load() <= 1.0f);
|
||||
sound->detachStream(90003);
|
||||
QTRY_VERIFY(std::abs(mixedMean.load() - 0.25f) < 0.001f);
|
||||
#else
|
||||
QSKIP("Shared output fixture requires null audio");
|
||||
#endif
|
||||
}
|
||||
|
||||
void independentReaders() {
|
||||
QVERIFY(gui_stream_open(201, group, inst->node_id, epoch));
|
||||
QVERIFY(gui_stream_open(202, group, inst->node_id, epoch));
|
||||
@@ -651,8 +698,15 @@ private slots:
|
||||
stream_info info{}; QCOMPARE(stream_get_info(publisher, &info), STREAM_OK);
|
||||
QVERIFY(gui_stream_open(105, group, inst->node_id, info.descriptor.source_epoch));
|
||||
StreamRecord event; QVERIFY(receive(105, event)); QCOMPARE(event.kind, StreamRecordKind::Opened);
|
||||
#ifdef HAVE_FFMPEG
|
||||
StreamPlayerWindow window(group, inst->node_id, info.descriptor.source_epoch, "Closing channel");
|
||||
QTRY_VERIFY(stream_get_info(publisher, &info) == STREAM_OK && info.owners == 3);
|
||||
#endif
|
||||
QByteArray channel = QByteArray::number(group); chat_core_delete_channel(inst, channel.constData());
|
||||
QVERIFY(receive(105, event)); QCOMPARE(event.kind, StreamRecordKind::Closed); QCOMPARE(event.reason, STREAM_CLOSE_GROUP);
|
||||
#ifdef HAVE_FFMPEG
|
||||
QTRY_VERIFY(window.finished()); QVERIFY(!window.failed()); // Закрытый канал не запускает бесконечный retry.
|
||||
#endif
|
||||
QTRY_COMPARE(snapshots.last().mid(9), QByteArray("[]"));
|
||||
QVERIFY(!stream_handle_active(publisher)); QCOMPARE(stream_handle_reason(publisher), STREAM_CLOSE_GROUP);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user