summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/android/audio/qandroidaudiosink.cpp')
-rw-r--r--src/multimedia/platform/android/audio/qandroidaudiosink.cpp636
1 files changed, 0 insertions, 636 deletions
diff --git a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp b/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
deleted file mode 100644
index 1a0b622a3..000000000
--- a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
+++ /dev/null
@@ -1,636 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidaudiosink_p.h"
-#include "qopenslesengine_p.h"
-#include <QDebug>
-#include <qmath.h>
-#include <qmediadevices.h>
-
-#ifdef ANDROID
-#include <SLES/OpenSLES_Android.h>
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-#endif // ANDROID
-
-#define BUFFER_COUNT 2
-
-QT_BEGIN_NAMESPACE
-
-static inline void openSlDebugInfo()
-{
- const QAudioFormat &format = QMediaDevices::defaultAudioOutput().preferredFormat();
- qDebug() << "======= OpenSL ES Device info ======="
- << "\nSupports low-latency playback: " << (QOpenSLESEngine::supportsLowLatency() ? "YES" : "NO")
- << "\nPreferred sample rate: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, -1)
- << "\nFrames per buffer: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, -1)
- << "\nPreferred Format: " << format
- << "\nLow-latency buffer size: " << QOpenSLESEngine::getLowLatencyBufferSize(format)
- << "\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)
-{
-#ifndef ANDROID
- m_streamType = -1;
-#else
- m_streamType = SL_ANDROID_STREAM_MEDIA;
-#endif // ANDROID
-}
-
-QAndroidAudioSink::~QAndroidAudioSink()
-{
- destroyPlayer();
-}
-
-QAudio::Error QAndroidAudioSink::error() const
-{
- return m_error;
-}
-
-QAudio::State QAndroidAudioSink::state() const
-{
- return m_state;
-}
-
-void QAndroidAudioSink::start(QIODevice *device)
-{
- Q_ASSERT(device);
-
- if (m_state != QAudio::StoppedState)
- stop();
-
- if (!preparePlayer())
- return;
-
- m_pullMode = true;
- m_audioSource = device;
- m_nextBuffer = 0;
- m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
-
- // Attempt to fill buffers first.
- for (int i = 0; i != BUFFER_COUNT; ++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,
- m_buffers + index,
- readSize)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
- m_processedBytes += readSize;
- }
-
- if (m_processedBytes < 1)
- onEOSEvent();
-
- // Change the state to playing.
- // We need to do this after filling the buffers or processedBytes might get corrupted.
- startPlayer();
-}
-
-QIODevice *QAndroidAudioSink::start()
-{
- if (m_state != QAudio::StoppedState)
- stop();
-
- if (!preparePlayer())
- return nullptr;
-
- m_pullMode = false;
- m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
- m_audioSource = new SLIODevicePrivate(this);
- m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
-
- // Change the state to playing
- startPlayer();
-
- setState(QAudio::IdleState);
- return m_audioSource;
-}
-
-void QAndroidAudioSink::stop()
-{
- if (m_state == QAudio::StoppedState)
- return;
-
- stopPlayer();
- setError(QAudio::NoError);
-}
-
-qsizetype QAndroidAudioSink::bytesFree() const
-{
- if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
- return 0;
-
- return m_availableBuffers.loadAcquire() ? m_bufferSize : 0;
-}
-
-void QAndroidAudioSink::setBufferSize(qsizetype value)
-{
- if (m_state != QAudio::StoppedState)
- return;
-
- m_startRequiresInit = true;
- m_bufferSize = value;
-}
-
-qsizetype QAndroidAudioSink::bufferSize() const
-{
- return m_bufferSize;
-}
-
-qint64 QAndroidAudioSink::processedUSecs() const
-{
- if (m_state == QAudio::IdleState || m_state == QAudio::SuspendedState)
- return m_format.durationForBytes(m_processedBytes);
-
- SLmillisecond processMSec = 0;
- if (m_playItf)
- (*m_playItf)->GetPosition(m_playItf, &processMSec);
-
- return processMSec * 1000;
-}
-
-void QAndroidAudioSink::resume()
-{
- if (m_state != QAudio::SuspendedState)
- return;
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
- setError(QAudio::NoError);
-}
-
-void QAndroidAudioSink::setFormat(const QAudioFormat &format)
-{
- m_startRequiresInit = true;
- m_format = format;
-}
-
-QAudioFormat QAndroidAudioSink::format() const
-{
- return m_format;
-}
-
-void QAndroidAudioSink::suspend()
-{
- if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
- return;
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PAUSED)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- setState(QAudio::SuspendedState);
- setError(QAudio::NoError);
-}
-
-void QAndroidAudioSink::reset()
-{
- destroyPlayer();
-}
-
-void QAndroidAudioSink::setVolume(qreal vol)
-{
- m_volume = qBound(qreal(0.0), vol, qreal(1.0));
- const SLmillibel newVolume = adjustVolume(m_volume);
- if (m_volumeItf && SL_RESULT_SUCCESS != (*m_volumeItf)->SetVolumeLevel(m_volumeItf, newVolume))
- qWarning() << "Unable to change volume";
-}
-
-qreal QAndroidAudioSink::volume() const
-{
- return m_volume;
-}
-
-void QAndroidAudioSink::onEOSEvent()
-{
- if (m_state != QAudio::ActiveState)
- return;
-
- SLBufferQueueState state;
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state))
- return;
-
- if (state.count > 0)
- return;
-
- setState(QAudio::IdleState);
- setError(QAudio::UnderrunError);
-}
-
-void QAndroidAudioSink::onBytesProcessed(qint64 bytes)
-{
- m_processedBytes += bytes;
-}
-
-void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex)
-{
- 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)
- QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
-
- return;
- }
-
- // We're in pull mode.
- const int index = m_nextBuffer * m_bufferSize;
- const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
-
- if (readSize < 1) {
- QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
- return;
- }
-
-
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
- m_buffers + index,
- readSize)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
- QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
-}
-
-void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
-{
- Q_UNUSED(player);
- QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx);
- if (event & SL_PLAYEVENT_HEADATEND)
- QMetaObject::invokeMethod(audioOutput, "onEOSEvent", Qt::QueuedConnection);
-}
-
-void QAndroidAudioSink::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx)
-{
- SLBufferQueueState state;
- (*bufferQueue)->GetState(bufferQueue, &state);
- QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx);
- audioOutput->bufferAvailable(state.count, state.playIndex);
-}
-
-bool QAndroidAudioSink::preparePlayer()
-{
- if (m_startRequiresInit)
- destroyPlayer();
- else
- return true;
-
- SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
- if (!engine) {
- qWarning() << "No engine";
- setError(QAudio::FatalError);
- return false;
- }
-
- SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT };
- SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
-
- SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat };
-
- // OutputMix
- if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine,
- &m_outputMixObject,
- 0,
- nullptr,
- nullptr)) {
- qWarning() << "Unable to create output mix";
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE)) {
- qWarning() << "Unable to initialize output mix";
- setError(QAudio::FatalError);
- return false;
- }
-
- SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject };
- SLDataSink audioSink = { &outputMixLocator, nullptr };
-
-#ifndef ANDROID
- const int iids = 2;
- const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME };
- const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
-#else
- const int iids = 3;
- const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE,
- SL_IID_VOLUME,
- SL_IID_ANDROIDCONFIGURATION };
- const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
-#endif // ANDROID
-
- // AudioPlayer
- if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine,
- &m_playerObject,
- &audioSrc,
- &audioSink,
- iids,
- ids,
- req)) {
- qWarning() << "Unable to create AudioPlayer";
- setError(QAudio::OpenError);
- return false;
- }
-
-#ifdef ANDROID
- // Set profile/category
- SLAndroidConfigurationItf playerConfig;
- if (SL_RESULT_SUCCESS == (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_ANDROIDCONFIGURATION,
- &playerConfig)) {
- (*playerConfig)->SetConfiguration(playerConfig,
- SL_ANDROID_KEY_STREAM_TYPE,
- &m_streamType,
- sizeof(SLint32));
- }
-#endif // ANDROID
-
- if (SL_RESULT_SUCCESS != (*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE)) {
- qWarning() << "Unable to initialize AudioPlayer";
- setError(QAudio::OpenError);
- return false;
- }
-
- // Buffer interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_BUFFERQUEUE,
- &m_bufferQueueItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf,
- bufferQueueCallback,
- this)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- // Play interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_PLAY,
- &m_playItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, m_eventMask)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- // Volume interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_VOLUME,
- &m_volumeItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- setVolume(m_volume);
-
- const int lowLatencyBufferSize = QOpenSLESEngine::getLowLatencyBufferSize(m_format);
- const int defaultBufferSize = QOpenSLESEngine::getDefaultBufferSize(m_format);
-
- if (defaultBufferSize <= 0) {
- qWarning() << "Unable to get minimum buffer size, returned" << defaultBufferSize;
- setError(QAudio::FatalError);
- return false;
- }
-
- // Buffer size
- if (m_bufferSize <= 0) {
- m_bufferSize = defaultBufferSize;
- } else if (QOpenSLESEngine::supportsLowLatency()) {
- if (m_bufferSize < lowLatencyBufferSize)
- m_bufferSize = lowLatencyBufferSize;
- } else if (m_bufferSize < defaultBufferSize) {
- m_bufferSize = defaultBufferSize;
- }
-
- if (!m_buffers)
- m_buffers = new char[BUFFER_COUNT * m_bufferSize];
-
- setError(QAudio::NoError);
- m_startRequiresInit = false;
-
- return true;
-}
-
-void QAndroidAudioSink::destroyPlayer()
-{
- if (m_state != QAudio::StoppedState)
- stopPlayer();
-
- if (m_playerObject) {
- (*m_playerObject)->Destroy(m_playerObject);
- m_playerObject = nullptr;
- }
-
- if (m_outputMixObject) {
- (*m_outputMixObject)->Destroy(m_outputMixObject);
- m_outputMixObject = nullptr;
- }
-
- if (!m_pullMode && m_audioSource) {
- m_audioSource->close();
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- delete [] m_buffers;
- m_buffers = nullptr;
- m_processedBytes = 0;
- m_nextBuffer = 0;
- m_availableBuffers.storeRelease(BUFFER_COUNT);
- m_playItf = nullptr;
- m_volumeItf = nullptr;
- m_bufferQueueItf = nullptr;
- m_startRequiresInit = true;
-}
-
-void QAndroidAudioSink::stopPlayer()
-{
- setState(QAudio::StoppedState);
-
- if (m_audioSource && !m_pullMode) {
- m_audioSource->close();
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- // 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 QAndroidAudioSink::startPlayer()
-{
- if (QOpenSLESEngine::printDebugInfo())
- openSlDebugInfo();
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- }
-}
-
-qint64 QAndroidAudioSink::writeData(const char *data, qint64 len)
-{
- 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 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);
- destroyPlayer();
- return -1;
- }
-
- m_processedBytes += len;
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
-
- return len;
-}
-
-inline void QAndroidAudioSink::setState(QAudio::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- Q_EMIT stateChanged(m_state);
-}
-
-inline void QAndroidAudioSink::setError(QAudio::Error error)
-{
- if (m_error == error)
- return;
-
- m_error = error;
- Q_EMIT errorChanged(m_error);
-}
-
-inline SLmillibel QAndroidAudioSink::adjustVolume(qreal vol)
-{
- if (qFuzzyIsNull(vol))
- return SL_MILLIBEL_MIN;
-
- if (qFuzzyCompare(vol, qreal(1.0)))
- return 0;
-
- return QAudio::convertVolume(vol, QAudio::LinearVolumeScale, QAudio::DecibelVolumeScale) * 100;
-}
-
-QT_END_NAMESPACE