OpenSL ES: improve buffer logic
authorChristian Strømme <christian.stromme@theqtcompany.com>
Thu, 30 Oct 2014 17:29:38 +0000 (18:29 +0100)
committerChristian Stromme <christian.stromme@digia.com>
Tue, 4 Nov 2014 14:09:26 +0000 (15:09 +0100)
Don't use relaxed load and stores, we need to be stricter to avoid
problems with high frequency re-fills of the buffer. If we don't enforce
ordering we might end-up spending more time trying to acquire an open
slot in the buffer. Updating processes bytes is also moved off the
"OpenSL" thread.

Added some comments for improved readability.

Change-Id: Ie27965fc6bf4b8394081ae6419f4933522ada98e
Reviewed-by: Yoann Lopes <yoann.lopes@theqtcompany.com>
src/plugins/opensles/qopenslesaudiooutput.cpp
src/plugins/opensles/qopenslesaudiooutput.h

index c45fbd3..b89d8b9 100644 (file)
@@ -157,7 +157,7 @@ int QOpenSLESAudioOutput::bytesFree() const
     if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
         return 0;
 
-    return m_availableBuffers.load() ? m_bufferSize : 0;
+    return m_availableBuffers.loadAcquire() ? m_bufferSize : 0;
 }
 
 int QOpenSLESAudioOutput::periodSize() const
@@ -343,6 +343,11 @@ void QOpenSLESAudioOutput::onEOSEvent()
     setError(QAudio::UnderrunError);
 }
 
+void QOpenSLESAudioOutput::onBytesProcessed(qint64 bytes)
+{
+    m_processedBytes += bytes;
+}
+
 void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
 {
     Q_UNUSED(count);
@@ -351,11 +356,13 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
     if (m_state == QAudio::StoppedState)
         return;
 
-    if (!m_pullMode) {
-        m_availableBuffers.fetchAndAddRelaxed(1);
+    if (!m_pullMode) { // We're in push mode.
+        // Signal that there is a new open slot in the buffer and return
+        m_availableBuffers.fetchAndAddRelease(1);
         return;
     }
 
+    // We're in pull mode.
     const int index = m_nextBuffer * m_bufferSize;
     const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
 
@@ -370,8 +377,8 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
         return;
     }
 
-    m_processedBytes += readSize;
     m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
+    QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
 }
 
 void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
@@ -570,7 +577,7 @@ void QOpenSLESAudioOutput::destroyPlayer()
     m_buffers = Q_NULLPTR;
     m_processedBytes = 0;
     m_nextBuffer = 0;
-    m_availableBuffers = BUFFER_COUNT;
+    m_availableBuffers.storeRelease(BUFFER_COUNT);
     m_playItf = Q_NULLPTR;
     m_volumeItf = Q_NULLPTR;
     m_bufferQueueItf = Q_NULLPTR;
@@ -599,20 +606,32 @@ void QOpenSLESAudioOutput::startPlayer()
 
 qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len)
 {
-    if (!len || !m_availableBuffers.load())
+    if (!len)
         return 0;
 
     if (len > m_bufferSize)
         len = m_bufferSize;
 
+    // Acquire one slot in the buffer
+    const int before = m_availableBuffers.fetchAndAddAcquire(-1);
+
+    // If there where no vacant slots, then we just overdrew the buffer account...
+    if (before < 1) {
+        m_availableBuffers.fetchAndAddRelease(1);
+        return 0;
+    }
+
     const int index = m_nextBuffer * m_bufferSize;
     ::memcpy(m_buffers + index, data, len);
     const SLuint32 res = (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
                                                       m_buffers + index,
                                                       len);
 
-    if (res == SL_RESULT_BUFFER_INSUFFICIENT)
+    // If we where unable to enqueue a new buffer, give back the acquired slot.
+    if (res == SL_RESULT_BUFFER_INSUFFICIENT) {
+        m_availableBuffers.fetchAndAddRelease(1);
         return 0;
+    }
 
     if (res != SL_RESULT_SUCCESS) {
         setError(QAudio::FatalError);
@@ -621,7 +640,6 @@ qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len)
     }
 
     m_processedBytes += len;
-    m_availableBuffers.fetchAndAddRelaxed(-1);
     setState(QAudio::ActiveState);
     setError(QAudio::NoError);
     m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
index 200b4a3..f36a5bf 100644 (file)
@@ -79,6 +79,7 @@ private:
     friend class SLIODevicePrivate;
 
     Q_INVOKABLE void onEOSEvent();
+    Q_INVOKABLE void onBytesProcessed(qint64 bytes);
     void bufferAvailable(quint32 count, quint32 playIndex);
 
     static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event);