From 21f1c0e2757d681d3b2af5a85b57f000c05f142a Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Sat, 13 Jan 2018 06:42:16 -0300 Subject: [PATCH] Android: Make audioInputStateChanged watcher work so it can restart input audio if it stops without a reason. Implement a mechanism that counts last reads in an interval and restart input audio in case of low reads -in these cases (happens particularly on Huawei) there is no call to the audioInputStateChanged method- (Android only) --- libraries/audio-client/src/AudioClient.cpp | 53 +++++++++++++++++++++- libraries/audio-client/src/AudioClient.h | 7 ++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index af86499101..6a7648fdc3 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -52,11 +52,21 @@ #include "AudioClient.h" +#if defined(Q_OS_ANDROID) +const int AudioClient::MIN_BUFFER_FRAMES = 20; +#else const int AudioClient::MIN_BUFFER_FRAMES = 1; +#endif + const int AudioClient::MAX_BUFFER_FRAMES = 20; static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100; +#if defined(Q_OS_ANDROID) +static const int CHECK_INPUT_READS_MSECS = 2000; +static const int MIN_READS_TO_CONSIDER_INPUT_ALIVE = 100; +#endif + static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; }; static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; }; @@ -197,6 +207,9 @@ AudioClient::AudioClient() : _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), _positionGetter(DEFAULT_POSITION_GETTER), +#if defined(Q_OS_ANDROID) + _checkInputTimer(this), +#endif _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback assert(_localSamplesAvailable.is_lock_free()); @@ -278,6 +291,9 @@ void AudioClient::cleanupBeforeQuit() { return; } +#if defined(Q_OS_ANDROID) + _shouldRestartInputSetup = false; +#endif stop(); _checkDevicesTimer->stop(); _checkPeakValuesTimer->stop(); @@ -622,6 +638,12 @@ void AudioClient::start() { qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format."; qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.nearestFormat(_desiredOutputFormat); } +#if defined(Q_OS_ANDROID) + connect(&_checkInputTimer, &QTimer::timeout, [this] { + checkInputTimeout(); + }); + _checkInputTimer.start(CHECK_INPUT_READS_MSECS); +#endif } void AudioClient::stop() { @@ -631,6 +653,9 @@ void AudioClient::stop() { qCDebug(audioclient) << "AudioClient::stop(), requesting switchOutputToAudioDevice() to shut down"; switchOutputToAudioDevice(QAudioDeviceInfo(), true); +#if defined(Q_OS_ANDROID) + _checkInputTimer.stop(); +#endif } void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer message) { @@ -1090,6 +1115,10 @@ void AudioClient::handleMicAudioInput() { return; } +#if defined(Q_OS_ANDROID) + _inputReadsSinceLastCheck++; +#endif + // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output const int inputSamplesRequired = (_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : @@ -1425,6 +1454,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn // NOTE: device start() uses the Qt internal device list Lock lock(_deviceMutex); +#if defined(Q_OS_ANDROID) + _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged +#endif + // cleanup any previously initialized device if (_audioInput) { // The call to stop() causes _inputDevice to be destructed. @@ -1505,7 +1538,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn #if defined(Q_OS_ANDROID) if (_audioInput) { - connect(_audioInput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioInputStateChanged(QAudio::State))); + _shouldRestartInputSetup = true; + connect(_audioInput, &QAudioInput::stateChanged, [this](QAudio::State state) { + audioInputStateChanged(state); + }); } #endif _inputDevice = _audioInput->start(); @@ -1553,10 +1589,11 @@ void AudioClient::audioInputStateChanged(QAudio::State state) { if (!_audioInput) { break; } - // Stopped on purpose + // Stopped on purpose if (_shouldRestartInputSetup) { Lock lock(_deviceMutex); _inputDevice = _audioInput->start(); + qCDebug(audioclient) << "[AUDIO-WA] Restarted _audioInput"; lock.unlock(); if (_inputDevice) { connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); @@ -1569,6 +1606,18 @@ void AudioClient::audioInputStateChanged(QAudio::State state) { break; } } + +void AudioClient::checkInputTimeout() { + if (_audioInput && _inputReadsSinceLastCheck < MIN_READS_TO_CONSIDER_INPUT_ALIVE) { + qCDebug(audioclient) << "[AUDIO-WA] No input in last second _inputReadsSinceLastCheck" << _inputReadsSinceLastCheck; + // this is a weird issue happening at low level in android + // it simply stops sending us mic data + _audioInput->stop(); + } else { + //qCDebug(audioclient) << "[AUDIO-WA] ok _inputReadsSinceLastCheck " << _inputReadsSinceLastCheck; + _inputReadsSinceLastCheck = 0; + } +} #endif void AudioClient::outputNotify() { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 0ceb9c4dc3..47c3171acd 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -26,7 +26,6 @@ #include #include #include - #include #include #include @@ -185,6 +184,7 @@ public slots: void handleMicAudioInput(); #if defined(Q_OS_ANDROID) void audioInputStateChanged(QAudio::State state); + void checkInputTimeout(); #endif void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); @@ -276,6 +276,11 @@ private: float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); +#ifdef Q_OS_ANDROID + QTimer _checkInputTimer; + long _inputReadsSinceLastCheck = 0l; +#endif + class Gate { public: Gate(AudioClient* audioClient);