summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMikko Hallamaa <mikko.hallamaa@qt.io>2023-12-05 13:03:05 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-12-21 16:55:18 +0000
commit20ecf1a9b9170f7cc0157cd94f71131d15cea4f0 (patch)
tree377998e124ef99b1d37c52b49da0192690d18268 /src
parentacdd194197bb9882c3ca74db541a17adfad8d207 (diff)
Fix PulseAudio sink intermittent hanging and glitching in pull mode
PulseAudio sink in pull mode asks the sound server for the amount of bytes it wants the client to write on the playback stream. Data was written from source in chunks of 20 milliseconds, without going over the requested amount. This however sometimes lead to a state where the server asks for less bytes than the sink considers as one chunk, which caused audio hang-ups. Now the PulseAudio sink will completely write the amount requested by the sound server. Additionally the audio source is reset when closing the sink to avoid glitches when playing audio from the same source later. The timing of chunk writes is adjusted according to the buffer target length in order to to lower the chance having to write a chunk in two parts. Fixes: QTBUG-116519 Pick-to: 6.6 6.5 Change-Id: I97816c0b7c3c98f3ff6a9d5b1329c616a0f00aa8 Reviewed-by: Artem Dyomin <artem.dyomin@qt.io> Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io> (cherry picked from commit 853d36bd80887bb3ba7503c162701b60841a06e2) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src')
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiosink.cpp37
1 files changed, 23 insertions, 14 deletions
diff --git a/src/multimedia/pulseaudio/qpulseaudiosink.cpp b/src/multimedia/pulseaudio/qpulseaudiosink.cpp
index d1da36739..de08a450e 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosink.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiosink.cpp
@@ -16,7 +16,7 @@
QT_BEGIN_NAMESPACE
-static constexpr int SinkPeriodTimeMs = 20;
+static constexpr uint SinkPeriodTimeMs = 20;
#define LOW_LATENCY_CATEGORY_NAME "game"
@@ -297,9 +297,11 @@ bool QPulseAudioSink::open()
pa_threaded_mainloop_wait(pulseEngine->mainloop());
const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream);
- m_periodTime = SinkPeriodTimeMs;
- m_periodSize = pa_usec_to_bytes(m_periodTime * 1000, &m_spec);
m_bufferSize = buffer->tlength;
+
+ // Adjust period time to reduce chance of it being higher than amount of bytes requested by PulseAudio server
+ m_periodTime = qMin(SinkPeriodTimeMs, pa_bytes_to_usec(m_bufferSize, &m_spec) / 1000 / 2);
+ m_periodSize = pa_usec_to_bytes(m_periodTime * 1000, &m_spec);
m_audioBuffer.resize(buffer->maxlength);
const qint64 streamSize = m_audioSource ? m_audioSource->size() : 0;
@@ -367,6 +369,7 @@ void QPulseAudioSink::close()
if (m_audioSource) {
if (m_pullMode) {
disconnect(m_audioSource, &QIODevice::readyRead, this, nullptr);
+ m_audioSource->reset();
} else {
delete m_audioSource;
m_audioSource = nullptr;
@@ -394,31 +397,37 @@ void QPulseAudioSink::userFeed()
if (m_pullMode) {
int writableSize = bytesFree();
- int chunks = writableSize / m_periodSize;
- if (chunks == 0) {
+
+ if (writableSize == 0)
+ {
+ // PulseAudio server doesn't want any more data
m_stateMachine.activateFromIdle();
return;
}
- const int input = std::min(m_periodSize, static_cast<int>(m_audioBuffer.size()));
+ // Write up to writableSize
+ const int inputSize = std::min({ m_periodSize, static_cast<int>(m_audioBuffer.size()), writableSize });
Q_ASSERT(!m_audioBuffer.empty());
- int audioBytesPulled = m_audioSource->read(m_audioBuffer.data(), input);
- Q_ASSERT(audioBytesPulled <= input);
+ int audioBytesPulled = m_audioSource->read(m_audioBuffer.data(), inputSize);
+ Q_ASSERT(audioBytesPulled <= inputSize);
+
if (audioBytesPulled > 0) {
- if (audioBytesPulled > input) {
+ if (audioBytesPulled > inputSize) {
qCWarning(qLcPulseAudioOut)
<< "Invalid audio data size provided by pull source:" << audioBytesPulled
- << "should be less than" << input;
- audioBytesPulled = input;
+ << "should be less than" << inputSize;
+ audioBytesPulled = inputSize;
}
auto bytesWritten = write(m_audioBuffer.data(), audioBytesPulled);
if (bytesWritten != audioBytesPulled)
- qWarning() << "Unfinished write should not happen since the data provided is "
- "less than writableSize:"
+ qWarning() << "Unfinished write:"
<< bytesWritten << "vs" << audioBytesPulled;
- if (chunks > 1) {
+ m_stateMachine.activateFromIdle();
+
+ if (inputSize < writableSize)
+ {
// PulseAudio needs more data. Ask for it immediately.
QMetaObject::invokeMethod(this, &QPulseAudioSink::userFeed, Qt::QueuedConnection);
}