diff options
Diffstat (limited to 'src/multimedia/android/qandroidaudiosink.cpp')
-rw-r--r-- | src/multimedia/android/qandroidaudiosink.cpp | 144 |
1 files changed, 61 insertions, 83 deletions
diff --git a/src/multimedia/android/qandroidaudiosink.cpp b/src/multimedia/android/qandroidaudiosink.cpp index bd7005a5b..4da4c5fdc 100644 --- a/src/multimedia/android/qandroidaudiosink.cpp +++ b/src/multimedia/android/qandroidaudiosink.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qandroidaudiosink_p.h" #include "qopenslesengine_p.h" @@ -48,8 +12,6 @@ #include <SLES/OpenSLES_AndroidConfiguration.h> #endif // ANDROID -#define BUFFER_COUNT 2 - QT_BEGIN_NAMESPACE static inline void openSlDebugInfo() @@ -64,26 +26,9 @@ static inline void openSlDebugInfo() << "\nDefault buffer size: " << QOpenSLESEngine::getDefaultBufferSize(format); } -QAndroidAudioSink::QAndroidAudioSink(const QByteArray &device) - : m_deviceName(device), - m_state(QAudio::StoppedState), - m_error(QAudio::NoError), - m_outputMixObject(nullptr), - m_playerObject(nullptr), - m_playItf(nullptr), - m_volumeItf(nullptr), - m_bufferQueueItf(nullptr), - m_audioSource(nullptr), - m_buffers(nullptr), - m_volume(1.0), - m_pullMode(false), - m_nextBuffer(0), - m_bufferSize(0), - m_elapsedTime(0), - m_processedBytes(0), - m_availableBuffers(BUFFER_COUNT), - m_eventMask(SL_PLAYEVENT_HEADATEND), - m_startRequiresInit(true) +QAndroidAudioSink::QAndroidAudioSink(const QByteArray &device, QObject *parent) + : QPlatformAudioSink(parent), + m_deviceName(device) { #ifndef ANDROID m_streamType = -1; @@ -117,16 +62,17 @@ void QAndroidAudioSink::start(QIODevice *device) if (!preparePlayer()) return; + m_endSound = false; m_pullMode = true; m_audioSource = device; m_nextBuffer = 0; m_processedBytes = 0; - m_availableBuffers = BUFFER_COUNT; + m_availableBuffers = BufferCount; setState(QAudio::ActiveState); setError(QAudio::NoError); // Attempt to fill buffers first. - for (int i = 0; i != BUFFER_COUNT; ++i) { + for (int i = 0; i != BufferCount; ++i) { const int index = i * m_bufferSize; const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); if (readSize && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, @@ -145,6 +91,16 @@ void QAndroidAudioSink::start(QIODevice *device) // Change the state to playing. // We need to do this after filling the buffers or processedBytes might get corrupted. startPlayer(); + connect(m_audioSource, &QIODevice::readyRead, this, &QAndroidAudioSink::readyRead); +} + +void QAndroidAudioSink::readyRead() +{ + if (m_pullMode && m_state == QAudio::IdleState) { + setState(QAudio::ActiveState); + setError(QAudio::NoError); + QMetaObject::invokeMethod(this, "bufferAvailable", Qt::QueuedConnection); + } } QIODevice *QAndroidAudioSink::start() @@ -157,7 +113,7 @@ QIODevice *QAndroidAudioSink::start() m_pullMode = false; m_processedBytes = 0; - m_availableBuffers = BUFFER_COUNT; + m_availableBuffers = BufferCount; m_audioSource = new SLIODevicePrivate(this); m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); @@ -222,7 +178,7 @@ void QAndroidAudioSink::resume() return; } - setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState); + setState(m_suspendedInState); setError(QAudio::NoError); } @@ -248,6 +204,7 @@ void QAndroidAudioSink::suspend() return; } + m_suspendedInState = m_state; setState(QAudio::SuspendedState); setError(QAudio::NoError); } @@ -291,18 +248,15 @@ void QAndroidAudioSink::onBytesProcessed(qint64 bytes) m_processedBytes += bytes; } -void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex) +void QAndroidAudioSink::bufferAvailable() { - Q_UNUSED(count); - Q_UNUSED(playIndex); - if (m_state == QAudio::StoppedState) return; if (!m_pullMode) { // We're in push mode. // Signal that there is a new open slot in the buffer and return const int val = m_availableBuffers.fetchAndAddRelease(1) + 1; - if (val == BUFFER_COUNT) + if (val == BufferCount) QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection); return; @@ -310,7 +264,23 @@ void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex) // We're in pull mode. const int index = m_nextBuffer * m_bufferSize; - const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + qint64 readSize = 0; + if (m_audioSource->atEnd()) { + // The whole sound was passed to player buffer, but setting SL_PLAYSTATE_STOPPED state + // too quickly will result in cutting of end of the sound. Therefore, we make it a little + // longer with empty data to make sure they will be played correctly + if (m_endSound) { + m_endSound = false; + setState(QAudio::IdleState); + return; + } + m_endSound = true; + readSize = m_bufferSize; + memset(m_buffers + index, 0, readSize); + } else { + readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + } + if (readSize < 1) { QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection); @@ -326,8 +296,10 @@ void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex) return; } - m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; - QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize)); + m_nextBuffer = (m_nextBuffer + 1) % BufferCount; + if (!m_endSound) { + QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize)); + } } void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event) @@ -340,10 +312,9 @@ void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event void QAndroidAudioSink::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx) { - SLBufferQueueState state; - (*bufferQueue)->GetState(bufferQueue, &state); + Q_UNUSED(bufferQueue); QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx); - audioOutput->bufferAvailable(state.count, state.playIndex); + QMetaObject::invokeMethod(audioOutput, "bufferAvailable", Qt::QueuedConnection); } bool QAndroidAudioSink::preparePlayer() @@ -353,6 +324,9 @@ bool QAndroidAudioSink::preparePlayer() else return true; + if (!QOpenSLESEngine::setAudioOutput(m_deviceName)) + qWarning() << "Unable to set up Audio Output Device"; + SLEngineItf engine = QOpenSLESEngine::instance()->slEngine(); if (!engine) { qWarning() << "No engine"; @@ -360,7 +334,7 @@ bool QAndroidAudioSink::preparePlayer() return false; } - SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT }; + SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BufferCount }; SLAndroidDataFormat_PCM_EX pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; @@ -492,7 +466,7 @@ bool QAndroidAudioSink::preparePlayer() } if (!m_buffers) - m_buffers = new char[BUFFER_COUNT * m_bufferSize]; + m_buffers = new char[BufferCount * m_bufferSize]; setError(QAudio::NoError); m_startRequiresInit = false; @@ -525,7 +499,7 @@ void QAndroidAudioSink::destroyPlayer() m_buffers = nullptr; m_processedBytes = 0; m_nextBuffer = 0; - m_availableBuffers.storeRelease(BUFFER_COUNT); + m_availableBuffers.storeRelease(BufferCount); m_playItf = nullptr; m_volumeItf = nullptr; m_bufferQueueItf = nullptr; @@ -536,10 +510,14 @@ void QAndroidAudioSink::stopPlayer() { setState(QAudio::StoppedState); - if (m_audioSource && !m_pullMode) { - m_audioSource->close(); - delete m_audioSource; - m_audioSource = nullptr; + if (m_audioSource) { + if (m_pullMode) { + disconnect(m_audioSource, &QIODevice::readyRead, this, &QAndroidAudioSink::readyRead); + } else { + m_audioSource->close(); + delete m_audioSource; + m_audioSource = nullptr; + } } // We need to change the state manually... @@ -599,7 +577,7 @@ qint64 QAndroidAudioSink::writeData(const char *data, qint64 len) m_processedBytes += len; setState(QAudio::ActiveState); setError(QAudio::NoError); - m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; + m_nextBuffer = (m_nextBuffer + 1) % BufferCount; return len; } |