diff options
Diffstat (limited to 'src/multimedia/android')
-rw-r--r-- | src/multimedia/android/qandroidaudiodevice.cpp | 40 | ||||
-rw-r--r-- | src/multimedia/android/qandroidaudiodevice_p.h | 40 | ||||
-rw-r--r-- | src/multimedia/android/qandroidaudiosink.cpp | 144 | ||||
-rw-r--r-- | src/multimedia/android/qandroidaudiosink_p.h | 85 | ||||
-rw-r--r-- | src/multimedia/android/qandroidaudiosource.cpp | 73 | ||||
-rw-r--r-- | src/multimedia/android/qandroidaudiosource_p.h | 42 | ||||
-rw-r--r-- | src/multimedia/android/qandroidmediadevices.cpp | 80 | ||||
-rw-r--r-- | src/multimedia/android/qandroidmediadevices_p.h | 47 | ||||
-rw-r--r-- | src/multimedia/android/qopenslesengine.cpp | 81 | ||||
-rw-r--r-- | src/multimedia/android/qopenslesengine_p.h | 41 |
10 files changed, 188 insertions, 485 deletions
diff --git a/src/multimedia/android/qandroidaudiodevice.cpp b/src/multimedia/android/qandroidaudiodevice.cpp index fcd979a5a..576774fd8 100644 --- a/src/multimedia/android/qandroidaudiodevice.cpp +++ b/src/multimedia/android/qandroidaudiodevice.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 "qandroidaudiodevice_p.h" diff --git a/src/multimedia/android/qandroidaudiodevice_p.h b/src/multimedia/android/qandroidaudiodevice_p.h index 39aec7311..d448306fe 100644 --- a/src/multimedia/android/qandroidaudiodevice_p.h +++ b/src/multimedia/android/qandroidaudiodevice_p.h @@ -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 #ifndef QOPENSLESDEVICEINFO_H #define QOPENSLESDEVICEINFO_H 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; } diff --git a/src/multimedia/android/qandroidaudiosink_p.h b/src/multimedia/android/qandroidaudiosink_p.h index 2a843eed8..4cb63b252 100644 --- a/src/multimedia/android/qandroidaudiosink_p.h +++ b/src/multimedia/android/qandroidaudiosink_p.h @@ -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 #ifndef QOPENSLESAUDIOOUTPUT_H #define QOPENSLESAUDIOOUTPUT_H @@ -65,7 +29,7 @@ class QAndroidAudioSink : public QPlatformAudioSink Q_OBJECT public: - QAndroidAudioSink(const QByteArray &device); + QAndroidAudioSink(const QByteArray &device, QObject *parent); ~QAndroidAudioSink(); void start(QIODevice *device) override; @@ -91,7 +55,7 @@ private: Q_INVOKABLE void onEOSEvent(); Q_INVOKABLE void onBytesProcessed(qint64 bytes); - void bufferAvailable(quint32 count, quint32 playIndex); + Q_INVOKABLE void bufferAvailable(); static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event); static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); @@ -99,6 +63,7 @@ private: bool preparePlayer(); void destroyPlayer(); void stopPlayer(); + void readyRead(); void startPlayer(); qint64 writeData(const char *data, qint64 len); @@ -107,25 +72,29 @@ private: SLmillibel adjustVolume(qreal vol); + static constexpr int BufferCount = 2; + QByteArray m_deviceName; - QAudio::State m_state; - QAudio::Error m_error; - SLObjectItf m_outputMixObject; - SLObjectItf m_playerObject; - SLPlayItf m_playItf; - SLVolumeItf m_volumeItf; - SLBufferQueueItf m_bufferQueueItf; - QIODevice *m_audioSource; - char *m_buffers; - qreal m_volume; - bool m_pullMode; - int m_nextBuffer; - int m_bufferSize; - qint64 m_elapsedTime; - qint64 m_processedBytes; - QAtomicInt m_availableBuffers; - SLuint32 m_eventMask; - bool m_startRequiresInit; + QAudio::State m_state = QAudio::StoppedState; + QAudio::State m_suspendedInState = QAudio::SuspendedState; + QAudio::Error m_error = QAudio::NoError; + SLObjectItf m_outputMixObject = nullptr; + SLObjectItf m_playerObject = nullptr; + SLPlayItf m_playItf = nullptr; + SLVolumeItf m_volumeItf = nullptr; + SLBufferQueueItf m_bufferQueueItf = nullptr; + QIODevice *m_audioSource = nullptr; + char *m_buffers = nullptr; + qreal m_volume = 1.0; + bool m_pullMode = false; + int m_nextBuffer = 0; + int m_bufferSize = 0; + qint64 m_elapsedTime = 0; + qint64 m_processedBytes = 0; + bool m_endSound = false; + QAtomicInt m_availableBuffers = BufferCount; + SLuint32 m_eventMask = SL_PLAYEVENT_HEADATEND; + bool m_startRequiresInit = true; qint32 m_streamType; QAudioFormat m_format; diff --git a/src/multimedia/android/qandroidaudiosource.cpp b/src/multimedia/android/qandroidaudiosource.cpp index 121c6294a..3a7332fd1 100644 --- a/src/multimedia/android/qandroidaudiosource.cpp +++ b/src/multimedia/android/qandroidaudiosource.cpp @@ -1,55 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 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 "qandroidaudiosource_p.h" #include "qopenslesengine_p.h" #include <private/qaudiohelpers_p.h> -#include <QtCore/private/qandroidextras_p.h> #include <qbuffer.h> #include <qdebug.h> +#include <qloggingcategory.h> #ifdef ANDROID #include <SLES/OpenSLES_AndroidConfiguration.h> #include <QtCore/qcoreapplication.h> +#include <QtCore/qpermissions.h> #endif +static Q_LOGGING_CATEGORY(qLcAndroidAudioSource, "qt.multimedia.android.audiosource") + QT_BEGIN_NAMESPACE #define NUM_BUFFERS 2 @@ -59,20 +26,13 @@ QT_BEGIN_NAMESPACE #ifdef ANDROID static bool hasRecordingPermission() { - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) - return true; - - const auto key = QStringLiteral("android.permission.RECORD_AUDIO"); - // Permission already granted? - if (QtAndroidPrivate::checkPermission(key).result() == QtAndroidPrivate::Authorized) - return true; + QMicrophonePermission permission; - if (QtAndroidPrivate::requestPermission(key).result() != QtAndroidPrivate::Authorized) { - qDebug("Microphone permission denied by user!"); - return false; - } + const bool permitted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted; + if (!permitted) + qCWarning(qLcAndroidAudioSource, "Missing microphone permission!"); - return true; + return permitted; } static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context) @@ -84,8 +44,9 @@ static void bufferQueueCallback(SLBufferQueueItf, void *context) QMetaObject::invokeMethod(reinterpret_cast<QAndroidAudioSource*>(context), "processBuffer"); } -QAndroidAudioSource::QAndroidAudioSource(const QByteArray &device) - : m_device(device) +QAndroidAudioSource::QAndroidAudioSource(const QByteArray &device, QObject *parent) + : QPlatformAudioSource(parent) + , m_device(device) , m_engine(QOpenSLESEngine::instance()) , m_recorderObject(0) , m_recorder(0) @@ -180,7 +141,7 @@ QIODevice *QAndroidAudioSource::start() m_pullMode = false; m_pushBuffer.clear(); - m_bufferIODevice = new QBuffer(&m_pushBuffer); + m_bufferIODevice = new QBuffer(&m_pushBuffer, this); m_bufferIODevice->open(QIODevice::ReadOnly); if (startRecording()) { @@ -398,6 +359,10 @@ void QAndroidAudioSource::processBuffer() QByteArray *processedBuffer = &m_buffers[m_currentBuffer]; writeDataToDevice(processedBuffer->constData(), processedBuffer->size()); + // Make sure that it was not stopped from writeDataToDevice + if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) + return; + // Re-enqueue the buffer SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue, processedBuffer->data(), diff --git a/src/multimedia/android/qandroidaudiosource_p.h b/src/multimedia/android/qandroidaudiosource_p.h index 727f1746f..13578509c 100644 --- a/src/multimedia/android/qandroidaudiosource_p.h +++ b/src/multimedia/android/qandroidaudiosource_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 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 #ifndef QOPENSLESAUDIOINPUT_H #define QOPENSLESAUDIOINPUT_H @@ -76,7 +40,7 @@ class QAndroidAudioSource : public QPlatformAudioSource Q_OBJECT public: - QAndroidAudioSource(const QByteArray &device); + QAndroidAudioSource(const QByteArray &device, QObject *parent); ~QAndroidAudioSource(); void start(QIODevice *device); diff --git a/src/multimedia/android/qandroidmediadevices.cpp b/src/multimedia/android/qandroidmediadevices.cpp index 8a38bef83..7688da079 100644 --- a/src/multimedia/android/qandroidmediadevices.cpp +++ b/src/multimedia/android/qandroidmediadevices.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 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 "qandroidmediadevices_p.h" #include "qmediadevices.h" @@ -53,7 +17,22 @@ QT_BEGIN_NAMESPACE -QAndroidMediaDevices::QAndroidMediaDevices() : QPlatformMediaDevices() { } +Q_DECLARE_JNI_CLASS(QtAudioDeviceManager, + "org/qtproject/qt/android/multimedia/QtAudioDeviceManager"); + + +QAndroidMediaDevices::QAndroidMediaDevices() : QPlatformMediaDevices() +{ + QtJniTypes::QtAudioDeviceManager::callStaticMethod<void>("registerAudioHeadsetStateReceiver"); +} + +QAndroidMediaDevices::~QAndroidMediaDevices() +{ + // Object of QAndroidMediaDevices type is static. Unregistering will happend only when closing + // the application. In such case it is probably not needed, but let's leave it for + // compatibility with Android documentation + QtJniTypes::QtAudioDeviceManager::callStaticMethod<void>("unregisterAudioHeadsetStateReceiver"); +} QList<QAudioDevice> QAndroidMediaDevices::audioInputs() const { @@ -65,34 +44,36 @@ QList<QAudioDevice> QAndroidMediaDevices::audioOutputs() const return QOpenSLESEngine::availableDevices(QAudioDevice::Output); } -QPlatformAudioSource *QAndroidMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) +QPlatformAudioSource *QAndroidMediaDevices::createAudioSource(const QAudioDevice &deviceInfo, + QObject *parent) { - return new QAndroidAudioSource(deviceInfo.id()); + return new QAndroidAudioSource(deviceInfo.id(), parent); } -QPlatformAudioSink *QAndroidMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) +QPlatformAudioSink *QAndroidMediaDevices::createAudioSink(const QAudioDevice &deviceInfo, + QObject *parent) { - return new QAndroidAudioSink(deviceInfo.id()); + return new QAndroidAudioSink(deviceInfo.id(), parent); } void QAndroidMediaDevices::forwardAudioOutputsChanged() { - audioOutputsChanged(); + emit audioOutputsChanged(); } void QAndroidMediaDevices::forwardAudioInputsChanged() { - audioInputsChanged(); + emit audioInputsChanged(); } static void onAudioInputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/) { - static_cast<QAndroidMediaDevices*>(QPlatformMediaDevices::instance())->forwardAudioInputsChanged(); + static_cast<QAndroidMediaDevices*>(QPlatformMediaIntegration::instance()->mediaDevices())->forwardAudioInputsChanged(); } static void onAudioOutputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/) { - static_cast<QAndroidMediaDevices*>(QPlatformMediaDevices::instance())->forwardAudioOutputsChanged(); + static_cast<QAndroidMediaDevices*>(QPlatformMediaIntegration::instance()->mediaDevices())->forwardAudioOutputsChanged(); } Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) @@ -126,11 +107,6 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) if (!registered) return JNI_ERR; - QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtAudioDeviceManager", - "registerAudioHeadsetStateReceiver", - "(Landroid/content/Context;)V", - QNativeInterface::QAndroidApplication::context()); - return JNI_VERSION_1_6; } diff --git a/src/multimedia/android/qandroidmediadevices_p.h b/src/multimedia/android/qandroidmediadevices_p.h index 25de67f54..a77ed0451 100644 --- a/src/multimedia/android/qandroidmediadevices_p.h +++ b/src/multimedia/android/qandroidmediadevices_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 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 #ifndef QANDROIDMEDIADEVICES_H #define QANDROIDMEDIADEVICES_H @@ -61,10 +25,13 @@ class QAndroidMediaDevices : public QPlatformMediaDevices public: QAndroidMediaDevices(); + ~QAndroidMediaDevices(); QList<QAudioDevice> audioInputs() const override; QList<QAudioDevice> audioOutputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; + QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo, + QObject *parent) override; + QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo, + QObject *parent) override; void forwardAudioOutputsChanged(); void forwardAudioInputsChanged(); diff --git a/src/multimedia/android/qopenslesengine.cpp b/src/multimedia/android/qopenslesengine.cpp index 94de56327..738161ab7 100644 --- a/src/multimedia/android/qopenslesengine.cpp +++ b/src/multimedia/android/qopenslesengine.cpp @@ -1,48 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 2021 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 "qopenslesengine_p.h" #include "qandroidaudiosource_p.h" #include "qandroidaudiodevice_p.h" +#include <QtCore/qcoreapplication.h> #include <QtCore/qjniobject.h> +#include <QtCore/qpermissions.h> #include <QtCore/private/qandroidextras_p.h> #include <qdebug.h> @@ -83,6 +49,22 @@ QOpenSLESEngine *QOpenSLESEngine::instance() return openslesEngine(); } +static SLuint32 getChannelMask(unsigned channelCount) +{ + switch (channelCount) { + case 1: return SL_SPEAKER_FRONT_CENTER; + case 2: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + case 3: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER; + case 4: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT + | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT; + case 5: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT + | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER; + case 6: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT + | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY; + default: return 0; // Default to 0 for an unsupported or unknown number of channels + } +} + SLAndroidDataFormat_PCM_EX QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format) { SLAndroidDataFormat_PCM_EX format_pcm; @@ -91,9 +73,7 @@ SLAndroidDataFormat_PCM_EX QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudi format_pcm.sampleRate = format.sampleRate() * 1000; format_pcm.bitsPerSample = format.bytesPerSample() * 8; format_pcm.containerSize = format.bytesPerSample() * 8; - format_pcm.channelMask = (format.channelCount() == 1 ? - SL_SPEAKER_FRONT_CENTER : - SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); + format_pcm.channelMask = getChannelMask(format_pcm.numChannels); format_pcm.endianness = (QSysInfo::ByteOrder == QSysInfo::LittleEndian ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN); @@ -147,15 +127,22 @@ QList<QAudioDevice> QOpenSLESEngine::availableDevices(QAudioDevice::Mode mode) return devices; } +bool QOpenSLESEngine::setAudioOutput(const QByteArray &deviceId) +{ + return QJniObject::callStaticMethod<jboolean>( + "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", + "setAudioOutput", + deviceId.toInt()); +} + static bool hasRecordPermission() { - const auto recordPerm = QtAndroidPrivate::checkPermission(QStringLiteral("android.permission.RECORD_AUDIO")); - return recordPerm.result() == QtAndroidPrivate::Authorized; + return qApp->checkPermission(QMicrophonePermission{}) == Qt::PermissionStatus::Granted; } QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) const { - if (mode == QAudioDevice::Input && hasRecordPermission()) { + if (mode == QAudioDevice::Input) { if (!m_checkedInputFormats) const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats(); return m_supportedInputChannelCounts; @@ -166,7 +153,7 @@ QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) cons QList<int> QOpenSLESEngine::supportedSampleRates(QAudioDevice::Mode mode) const { - if (mode == QAudioDevice::Input && hasRecordPermission()) { + if (mode == QAudioDevice::Input) { if (!m_checkedInputFormats) const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats(); return m_supportedInputSampleRates; @@ -374,6 +361,10 @@ bool QOpenSLESEngine::inputFormatIsSupported(SLAndroidDataFormat_PCM_EX format) SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; SLDataSink audioSnk = { &loc_bq, &format }; + // only ask permission when it is about to create the audiorecorder + if (!hasRecordPermission()) + return false; + result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0); if (result == SL_RESULT_SUCCESS) result = (*recorder)->Realize(recorder, false); diff --git a/src/multimedia/android/qopenslesengine_p.h b/src/multimedia/android/qopenslesengine_p.h index 3adf34594..9918ac888 100644 --- a/src/multimedia/android/qopenslesengine_p.h +++ b/src/multimedia/android/qopenslesengine_p.h @@ -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 #ifndef QOPENSLESENGINE_H #define QOPENSLESENGINE_H @@ -75,6 +39,7 @@ public: static SLAndroidDataFormat_PCM_EX audioFormatToSLFormatPCM(const QAudioFormat &format); static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode); + static bool setAudioOutput(const QByteArray &deviceId); QList<int> supportedChannelCounts(QAudioDevice::Mode mode) const; QList<int> supportedSampleRates(QAudioDevice::Mode mode) const; |