|
|
|
|
@ -64,12 +64,17 @@ class StreamPublishTest : public QObject {
|
|
|
|
|
uint64_t group = 0; |
|
|
|
|
size_t allocations = 0; |
|
|
|
|
static QByteArray keyframe(int codec, int width = 160, int height = 90, uint64_t pts = 100000) { |
|
|
|
|
#ifndef HAVE_FFMPEG |
|
|
|
|
Q_UNUSED(codec); Q_UNUSED(width); Q_UNUSED(height); Q_UNUSED(pts); |
|
|
|
|
return QByteArray(4, '\0') + "opaque access unit"; |
|
|
|
|
#else |
|
|
|
|
StreamVideoEncoder encoder; StreamVideoConfig config; config.width = width; config.height = height; config.codec = codec; |
|
|
|
|
if (!encoder.open(config)) return {}; |
|
|
|
|
QImage image(width, height, QImage::Format_RGB32); image.fill(QColor(45, 130, 220)); |
|
|
|
|
QList<StreamEncodedFrame> output; |
|
|
|
|
if (!encoder.encode(image, pts, true, output) || output.isEmpty() || !output.front().key) return {}; |
|
|
|
|
return output.front().data; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
bool record(uint64_t id, StreamRecordKind kind, StreamRecord& out) { |
|
|
|
|
QElapsedTimer elapsed; elapsed.start(); |
|
|
|
|
@ -110,11 +115,14 @@ private slots:
|
|
|
|
|
} |
|
|
|
|
void encoderRoundTrip_data() { |
|
|
|
|
QTest::addColumn<int>("codec"); |
|
|
|
|
for (int codec : StreamVideoEncoder::available()) QTest::newRow(qPrintable(StreamVideoEncoder::codecName(codec))) << codec; |
|
|
|
|
auto codecs = StreamVideoEncoder::available(); |
|
|
|
|
if (codecs.isEmpty()) QTest::newRow("unavailable") << 0; |
|
|
|
|
for (int codec : codecs) QTest::newRow(qPrintable(StreamVideoEncoder::codecName(codec))) << codec; |
|
|
|
|
} |
|
|
|
|
void encoderRoundTrip() { |
|
|
|
|
#ifdef HAVE_FFMPEG |
|
|
|
|
QFETCH(int, codec); auto encoded = keyframe(codec); QVERIFY(!encoded.isEmpty()); |
|
|
|
|
QFETCH(int, codec); if (!codec) QSKIP("No supported encoder"); |
|
|
|
|
auto encoded = keyframe(codec); QVERIFY(!encoded.isEmpty()); |
|
|
|
|
const uint8_t *config = nullptr, *frame = nullptr; size_t configLength = 0, frameLength = 0; |
|
|
|
|
QCOMPARE(stream_media_key_parse((const uint8_t*)encoded.constData(), encoded.size(), &config, &configLength, &frame, &frameLength), STREAM_OK); |
|
|
|
|
const char* names[] = {"", "h264", "hevc", "vp9", "av1", "vvc"}; |
|
|
|
|
@ -180,14 +188,14 @@ private slots:
|
|
|
|
|
QSignalSpy errors(&engine, &StreamPublishEngine::errorOccurred); |
|
|
|
|
QVERIFY(engine.start(group, config)); |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(engine.config().video.codec == STREAM_MEDIA_H264, 10000); |
|
|
|
|
char* sources = nullptr; uint64_t epoch = 0; |
|
|
|
|
uint64_t epoch = 0; |
|
|
|
|
QElapsedTimer wait; wait.start(); |
|
|
|
|
while (!epoch && wait.elapsed() < 5000) { |
|
|
|
|
stream_member_state member{}; |
|
|
|
|
if (stream_member_load(inst, group, inst->node_id, &member, nullptr) == STREAM_OK && member.published) epoch = member.source.source_epoch; |
|
|
|
|
QTest::qWait(1); |
|
|
|
|
} |
|
|
|
|
Q_UNUSED(sources); QVERIFY(epoch); QVERIFY(gui_stream_open(202, group, inst->node_id, epoch)); |
|
|
|
|
QVERIFY(epoch); QVERIFY(gui_stream_open(202, group, inst->node_id, epoch)); |
|
|
|
|
StreamRecord out; QVERIFY(record(202, StreamRecordKind::Opened, out)); QVERIFY(record(202, StreamRecordKind::Video, out)); |
|
|
|
|
uint64_t first = out.pts; unsigned audio = 0, video = 0; uint64_t latest = first, left = 0, right = 0; |
|
|
|
|
auto* decoder = opus_codec_decoder_create(48000, 2); QVERIFY(decoder); |
|
|
|
|
@ -236,7 +244,7 @@ private slots:
|
|
|
|
|
QVERIFY(!gui_stream_ready()); QVERIFY(gui_stream_attach(inst)); |
|
|
|
|
} |
|
|
|
|
void cleanupTestCase() { |
|
|
|
|
poll.stop(); gui_stream_detach(inst); uasync_mark_stopped(ua); utun_instance_destroy(inst); uasync_destroy(ua); |
|
|
|
|
poll.stop(); gui_stream_detach(inst); uasync_mark_stopped(ua); utun_instance_destroy(inst); uasync_destroy(ua, 1); |
|
|
|
|
QCOMPARE(u_get_allocated_count(), allocations); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|