diff options
author | Mikko Hallamaa <mikko.hallamaa@qt.io> | 2023-12-05 13:03:05 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-12-21 16:55:18 +0000 |
commit | 20ecf1a9b9170f7cc0157cd94f71131d15cea4f0 (patch) | |
tree | 377998e124ef99b1d37c52b49da0192690d18268 /src | |
parent | acdd194197bb9882c3ca74db541a17adfad8d207 (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.cpp | 37 |
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); } |