|
|
|
|
@ -30,6 +30,7 @@ static QList<QByteArray> snapshots;
|
|
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|
static std::atomic<ma_device_data_proc> originalAudioCallback{nullptr}; |
|
|
|
|
static std::atomic<uint64_t> nonzeroSamples{0}; |
|
|
|
|
static std::atomic<uint64_t> leftEnergy{0}, rightEnergy{0}; |
|
|
|
|
static std::atomic<int64_t> playbackDelayUs{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) { |
|
|
|
|
@ -46,11 +47,17 @@ extern "C" ma_result __wrap_ma_context_init(const ma_backend*, ma_uint32, const
|
|
|
|
|
static void observeAudio(ma_device* device, void* out, const void* in, ma_uint32 frames) { |
|
|
|
|
originalAudioCallback.load()(device, out, in, frames); |
|
|
|
|
const auto* pcm = static_cast<const int16_t*>(out); |
|
|
|
|
for (unsigned i = 0; i < frames; i++) if (pcm[i]) nonzeroSamples.fetch_add(1); |
|
|
|
|
uint64_t left = 0, right = 0, nonzero = 0; |
|
|
|
|
for (unsigned i = 0; i < frames; i++) { |
|
|
|
|
left += static_cast<int64_t>(pcm[2 * i]) * pcm[2 * i]; |
|
|
|
|
right += static_cast<int64_t>(pcm[2 * i + 1]) * pcm[2 * i + 1]; |
|
|
|
|
nonzero += pcm[2 * i] != 0; nonzero += pcm[2 * i + 1] != 0; |
|
|
|
|
} |
|
|
|
|
leftEnergy.fetch_add(left); rightEnergy.fetch_add(right); nonzeroSamples.fetch_add(nonzero); |
|
|
|
|
} |
|
|
|
|
extern "C" ma_result __wrap_ma_device_init(ma_context* context, const ma_device_config* config, ma_device* device) { |
|
|
|
|
ma_device_config copy = *config; |
|
|
|
|
if (copy.deviceType == ma_device_type_playback && copy.playback.format == ma_format_s16 && copy.playback.channels == 1) { |
|
|
|
|
if (copy.deviceType == ma_device_type_playback && copy.playback.format == ma_format_s16 && copy.playback.channels == 2) { |
|
|
|
|
originalAudioCallback = copy.dataCallback; copy.dataCallback = observeAudio; |
|
|
|
|
} |
|
|
|
|
return __real_ma_device_init(context, ©, device); |
|
|
|
|
@ -81,7 +88,7 @@ class StreamViewTest : public QObject {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void publish(const QByteArray& video, const QByteArray& audio = {}, unsigned videoFrames = 1) { |
|
|
|
|
stream_media_block media{}; media.format = {160, 90, rotation, STREAM_MEDIA_H264, STREAM_MEDIA_OPUS, revision}; |
|
|
|
|
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; |
|
|
|
|
for (unsigned i = 0; i < videoFrames; i++) media.video[i] = {++frameId, mediaPts + i * 100000 / videoFrames, |
|
|
|
|
@ -172,11 +179,17 @@ private slots:
|
|
|
|
|
QByteArray audio; |
|
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|
QVERIFY(SoundManager::instance()->init()); |
|
|
|
|
auto* opus = opus_codec_encoder_create(48000, 1); QVERIFY(opus); |
|
|
|
|
int16_t pcm[960]; for (unsigned i = 0; i < 960; i++) pcm[i] = static_cast<int16_t>(6000 * std::sin(i * 0.05)); |
|
|
|
|
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); |
|
|
|
|
nonzeroSamples = 0; playbackDelayUs = 200000; |
|
|
|
|
nonzeroSamples = 0; leftEnergy = rightEnergy = 0; playbackDelayUs = 200000; |
|
|
|
|
#endif |
|
|
|
|
StreamPlayerWindow window(group, inst->node_id, epoch, "Source"); |
|
|
|
|
QSignalSpy shown(&window, &StreamPlayerWindow::framePresented); window.show(); |
|
|
|
|
@ -190,6 +203,9 @@ private slots:
|
|
|
|
|
QCOMPARE(switched.descriptor.source_epoch, epoch); QCOMPARE(switched.owners, 2u); |
|
|
|
|
#ifdef TEST_NULL_AUDIO |
|
|
|
|
QTRY_VERIFY_WITH_TIMEOUT(nonzeroSamples.load() > 0, 5000); |
|
|
|
|
QVERIFY(rightEnergy.load() > 0); |
|
|
|
|
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); |
|
|
|
|
QTest::qWait(50); uint64_t played = nonzeroSamples.load(); QTest::qWait(100); QCOMPARE(nonzeroSamples.load(), played); |
|
|
|
|
#endif |
|
|
|
|
|