OpenSL ES: Be less rigid about tearing down the output device.
authorChristian Strømme <christian.stromme@digia.com>
Wed, 10 Sep 2014 09:52:28 +0000 (11:52 +0200)
committerChristian Stromme <christian.stromme@digia.com>
Fri, 12 Sep 2014 12:04:08 +0000 (14:04 +0200)
We where very strict about tearing down the audio device. While this is
a good strategy to avoid unnecessary resource usage, it also causes
excessive re-allocations, e.g., when transiting from start to stop and
back again. This can increase latency, especially in case where a short
clip is re-played at a high frequency.
This change also decrease the chance of the player ending up in some
unknown state where it drops audio clips without any warning.

Task-number: QTBUG-40864
Change-Id: I1afad4af0622983f0f0c221d91cf794585d8cad2
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
src/plugins/opensles/qopenslesaudiooutput.cpp
src/plugins/opensles/qopenslesaudiooutput.h

index f6583e5..06f2261 100644 (file)
@@ -71,7 +71,8 @@ QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device)
       m_elapsedTime(0),
       m_processedBytes(0),
       m_availableBuffers(BUFFER_COUNT),
-      m_eventMask(SL_PLAYEVENT_HEADATEND)
+      m_eventMask(SL_PLAYEVENT_HEADATEND),
+      m_startRequiresInit(true)
 {
 #ifndef ANDROID
       m_streamType = -1;
@@ -99,13 +100,10 @@ QAudio::State QOpenSLESAudioOutput::state() const
 void QOpenSLESAudioOutput::start(QIODevice *device)
 {
     Q_ASSERT(device);
-    destroyPlayer();
-
-    m_pullMode = true;
-
     if (!preparePlayer())
         return;
 
+    m_pullMode = true;
     m_audioSource = device;
     setState(QAudio::ActiveState);
     setError(QAudio::NoError);
@@ -126,29 +124,20 @@ void QOpenSLESAudioOutput::start(QIODevice *device)
 
     // Change the state to playing.
     // We need to do this after filling the buffers or processedBytes might get corrupted.
-    if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
-        setError(QAudio::FatalError);
-        destroyPlayer();
-    }
+    startPlayer();
 }
 
 QIODevice *QOpenSLESAudioOutput::start()
 {
-    destroyPlayer();
-
-    m_pullMode = false;
-
     if (!preparePlayer())
         return Q_NULLPTR;
 
+    m_pullMode = false;
     m_audioSource = new SLIODevicePrivate(this);
     m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
 
     // Change the state to playing
-    if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
-        setError(QAudio::FatalError);
-        destroyPlayer();
-    }
+    startPlayer();
 
     setState(QAudio::IdleState);
     return m_audioSource;
@@ -159,7 +148,7 @@ void QOpenSLESAudioOutput::stop()
     if (m_state == QAudio::StoppedState)
         return;
 
-    destroyPlayer();
+    stopPlayer();
     setError(QAudio::NoError);
 }
 
@@ -181,6 +170,7 @@ void QOpenSLESAudioOutput::setBufferSize(int value)
     if (m_state != QAudio::StoppedState)
         return;
 
+    m_startRequiresInit = true;
     m_bufferSize = value;
 }
 
@@ -254,6 +244,7 @@ void QOpenSLESAudioOutput::resume()
 
 void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format)
 {
+    m_startRequiresInit = true;
     m_format = format;
 }
 
@@ -325,6 +316,7 @@ void QOpenSLESAudioOutput::setCategory(const QString &category)
         return;
     }
 
+    m_startRequiresInit = true;
     m_streamType = streamType;
     m_category = category;
 #endif // ANDROID
@@ -403,6 +395,11 @@ void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, voi
 
 bool QOpenSLESAudioOutput::preparePlayer()
 {
+    if (m_startRequiresInit)
+        destroyPlayer();
+    else
+        return true;
+
     SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
     if (!engine) {
         qWarning() << "No engine";
@@ -543,20 +540,15 @@ bool QOpenSLESAudioOutput::preparePlayer()
 
     m_clockStamp.restart();
     setError(QAudio::NoError);
+    m_startRequiresInit = false;
 
     return true;
 }
 
 void QOpenSLESAudioOutput::destroyPlayer()
 {
-    setState(QAudio::StoppedState);
-
-    // We need to change the state manually...
-    if (m_playItf)
-        (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
-
-    if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
-        qWarning() << "Unable to clear buffer";
+    if (m_state != QAudio::StoppedState)
+        stopPlayer();
 
     if (m_playerObject) {
         (*m_playerObject)->Destroy(m_playerObject);
@@ -582,6 +574,27 @@ void QOpenSLESAudioOutput::destroyPlayer()
     m_playItf = Q_NULLPTR;
     m_volumeItf = Q_NULLPTR;
     m_bufferQueueItf = Q_NULLPTR;
+    m_startRequiresInit = true;
+}
+
+void QOpenSLESAudioOutput::stopPlayer()
+{
+    setState(QAudio::StoppedState);
+
+    // We need to change the state manually...
+    if (m_playItf)
+        (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
+
+    if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
+        qWarning() << "Unable to clear buffer";
+}
+
+void QOpenSLESAudioOutput::startPlayer()
+{
+    if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
+        setError(QAudio::FatalError);
+        destroyPlayer();
+    }
 }
 
 qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len)
index d466ea6..200b4a3 100644 (file)
@@ -86,6 +86,8 @@ private:
 
     bool preparePlayer();
     void destroyPlayer();
+    void stopPlayer();
+    void startPlayer();
     qint64 writeData(const char *data, qint64 len);
 
     void setState(QAudio::State state);
@@ -113,6 +115,7 @@ private:
     qint64 m_processedBytes;
     QAtomicInt m_availableBuffers;
     SLuint32 m_eventMask;
+    bool m_startRequiresInit;
 
     qint32 m_streamType;
     QTime m_clockStamp;