|
|
|
|
@ -87,7 +87,7 @@ class StreamViewTest : public QObject {
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop test missing record id=%llu", (unsigned long long)id); return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void publish(const QByteArray& video, const QByteArray& audio = {}, unsigned videoFrames = 1) { |
|
|
|
|
void publish(const QByteArray& video, const QList<QByteArray>& audio = {}, unsigned videoFrames = 1) { |
|
|
|
|
stream_media_block media{}; media.format = {160, 90, rotation, STREAM_MEDIA_H264, STREAM_MEDIA_OPUS_STEREO, revision}; |
|
|
|
|
mediaPts += 100000; |
|
|
|
|
media.first_frame_offset = 0; media.video_count = videoFrames; |
|
|
|
|
@ -95,9 +95,9 @@ class StreamViewTest : public QObject {
|
|
|
|
|
static_cast<uint32_t>(video.size()), 0, static_cast<uint32_t>(video.size()), STREAM_MEDIA_KEY, |
|
|
|
|
reinterpret_cast<const uint8_t*>(video.constData())}; |
|
|
|
|
if (!audio.isEmpty()) { |
|
|
|
|
media.audio_count = videoFrames > 1 ? 5 : 1; |
|
|
|
|
media.audio_count = audio.size(); |
|
|
|
|
for (unsigned i = 0; i < media.audio_count; i++) media.audio[i] = {mediaPts + i * 20000, |
|
|
|
|
static_cast<uint32_t>(audio.size()), 960, reinterpret_cast<const uint8_t*>(audio.constData())}; |
|
|
|
|
static_cast<uint32_t>(audio[i].size()), 960, reinterpret_cast<const uint8_t*>(audio[i].constData())}; |
|
|
|
|
} |
|
|
|
|
uint8_t wire[7080]; size_t length = 0; |
|
|
|
|
QCOMPARE(stream_media_encode(&media, wire, sizeof(wire), &length), STREAM_OK); |
|
|
|
|
@ -146,7 +146,7 @@ private slots:
|
|
|
|
|
void mediaAndPressure() { |
|
|
|
|
QVERIFY(gui_stream_open(102, group, inst->node_id, epoch)); StreamRecord event; |
|
|
|
|
QVERIFY(receive(102, event)); QCOMPARE(event.kind, StreamRecordKind::Opened); |
|
|
|
|
const QByteArray frame(1500, 'k'); const QByteArray audio("opus"); publish(frame, audio); |
|
|
|
|
const QByteArray frame(1500, 'k'); const QByteArray audio("opus"); publish(frame, {audio}); |
|
|
|
|
QVERIFY(receive(102, event)); QCOMPARE(event.kind, StreamRecordKind::Video); QCOMPARE(event.data, frame); |
|
|
|
|
QCOMPARE(event.format.rotation, uint16_t(90)); |
|
|
|
|
QVERIFY(receive(102, event)); QCOMPARE(event.kind, StreamRecordKind::Audio); QCOMPARE(event.data, audio); |
|
|
|
|
@ -176,24 +176,33 @@ private slots:
|
|
|
|
|
AVPacket* packet = av_packet_alloc(); QVERIFY(packet); QCOMPARE(avcodec_receive_packet(context, packet), 0); |
|
|
|
|
QByteArray video(reinterpret_cast<const char*>(packet->data), packet->size); |
|
|
|
|
av_packet_free(&packet); av_frame_free(&frame); avcodec_free_context(&context); |
|
|
|
|
QByteArray audio; |
|
|
|
|
std::unique_ptr<opus_codec_encoder_t, decltype(&opus_codec_encoder_destroy)> opus(nullptr, opus_codec_encoder_destroy); |
|
|
|
|
uint64_t sampleFrame = 0; |
|
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|
QVERIFY(SoundManager::instance()->init()); |
|
|
|
|
auto* opus = opus_codec_encoder_create(48000, 2); QVERIFY(opus); |
|
|
|
|
QCOMPARE(opus_codec_encoder_application_set(opus, OPUS_CODEC_APP_AUDIO), 0); |
|
|
|
|
QCOMPARE(opus_codec_encoder_bitrate_set(opus, 128000), 0); |
|
|
|
|
int16_t pcm[1920]; |
|
|
|
|
for (unsigned i = 0; i < 960; i++) { |
|
|
|
|
pcm[2 * i] = static_cast<int16_t>(6000 * std::sin(i * 0.05)); |
|
|
|
|
pcm[2 * i + 1] = static_cast<int16_t>(1500 * std::sin(i * 0.13)); |
|
|
|
|
} |
|
|
|
|
uint8_t encoded[1275]; int length = opus_codec_encode(opus, pcm, 960, encoded, sizeof(encoded)); QVERIFY(length > 0); |
|
|
|
|
audio = QByteArray(reinterpret_cast<const char*>(encoded), length); opus_codec_encoder_destroy(opus); |
|
|
|
|
opus.reset(opus_codec_encoder_create(48000, 2)); QVERIFY(opus); |
|
|
|
|
QCOMPARE(opus_codec_encoder_application_set(opus.get(), OPUS_CODEC_APP_AUDIO), 0); |
|
|
|
|
QCOMPARE(opus_codec_encoder_bitrate_set(opus.get(), 128000), 0); |
|
|
|
|
nonzeroSamples = 0; leftEnergy = rightEnergy = 0; playbackDelayUs = 200000; |
|
|
|
|
#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, audio] { publish(video, audio, 3); }); sender.start(100); |
|
|
|
|
QTimer sender; |
|
|
|
|
connect(&sender, &QTimer::timeout, this, [this, video, &opus, &sampleFrame] { |
|
|
|
|
QList<QByteArray> packets; |
|
|
|
|
if (opus) for (unsigned frame = 0; frame < 5; frame++) { |
|
|
|
|
int16_t pcm[1920]; uint8_t encoded[1275]; |
|
|
|
|
for (unsigned i = 0; i < 960; i++) { |
|
|
|
|
pcm[2 * i] = static_cast<int16_t>(6000 * std::sin((sampleFrame + i) * 0.05)); |
|
|
|
|
pcm[2 * i + 1] = static_cast<int16_t>(1500 * std::sin((sampleFrame + i) * 0.13)); |
|
|
|
|
} |
|
|
|
|
sampleFrame += 960; |
|
|
|
|
int length = opus_codec_encode(opus.get(), pcm, 960, encoded, sizeof(encoded)); QVERIFY(length > 0); |
|
|
|
|
packets.append(QByteArray(reinterpret_cast<const char*>(encoded), length)); |
|
|
|
|
} |
|
|
|
|
publish(video, packets, 3); |
|
|
|
|
}); |
|
|
|
|
sender.start(100); |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(shown.count() > 0, 5000); |
|
|
|
|
int beforeSteady = shown.count(); QTest::qWait(1500); |
|
|
|
|
QVERIFY2(shown.count() >= beforeSteady + 30, "30fps video must keep advancing with 200ms physical audio latency"); |
|
|
|
|
@ -204,6 +213,9 @@ private slots:
|
|
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(nonzeroSamples.load() > 0, 5000); |
|
|
|
|
QVERIFY(rightEnergy.load() > 0); |
|
|
|
|
qInfo("Stereo playback energies: left=%llu right=%llu ratio=%.3f", |
|
|
|
|
(unsigned long long)leftEnergy.load(), (unsigned long long)rightEnergy.load(), |
|
|
|
|
static_cast<double>(leftEnergy.load()) / rightEnergy.load()); |
|
|
|
|
QVERIFY2(leftEnergy.load() > rightEnergy.load() * 8 && leftEnergy.load() < rightEnergy.load() * 25, |
|
|
|
|
"Independent stereo channels must reach the playback device without downmix"); |
|
|
|
|
auto* volume = window.findChild<QSlider*>(); QVERIFY(volume); volume->setValue(0); |
|
|
|
|
|