Files
rtphone/src/engine/audio/Audio_Android.cpp
T

594 lines
19 KiB
C++

#include "Audio_Android.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Log.h"
#include <mutex>
#include <iostream>
#include "../helper/HL_String.h"
#ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator() {}
AndroidEnumerator::~AndroidEnumerator() {}
int AndroidEnumerator::indexOfDefaultDevice()
{
return 0;
}
int AndroidEnumerator::count()
{
return 1;
}
int AndroidEnumerator::idAt(int index)
{
return 0;
}
std::string AndroidEnumerator::nameAt(int index)
{
return "Audio";
}
void AndroidEnumerator::open(int direction) {}
void AndroidEnumerator::close() {}
// -----------------------
OpenSLEngine::OpenSLEngine() {}
OpenSLEngine::~OpenSLEngine() {}
void OpenSLEngine::open()
{
std::unique_lock<std::mutex> l(mMutex);
if (++mUsageCounter == 1)
internalOpen();
}
void OpenSLEngine::close()
{
std::unique_lock<std::mutex> l(mMutex);
if (mUsageCounter == 0)
return;
if (--mUsageCounter == 0)
internalClose();
}
#define CHECK_OPENSLES_ERROR \
if (resultCode != SL_RESULT_SUCCESS) \
throw Exception(ERR_OPENSLES, (int)resultCode)
void OpenSLEngine::internalOpen()
{
SLresult resultCode;
// Instantiate OpenSL ES engine object
resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr);
CHECK_OPENSLES_ERROR;
// Bring it online (realize)
resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Get interface finally
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
CHECK_OPENSLES_ERROR;
ICELogInfo(<< "OpenSL engine object created.");
}
void OpenSLEngine::internalClose()
{
if (mEngineObject != nullptr)
{
ICELogInfo(<< "Destroy OpenSL engine object.");
(*mEngineObject)->Destroy(mEngineObject);
mEngineObject = nullptr;
mEngineInterface = nullptr;
}
}
SLEngineItf OpenSLEngine::getNativeEngine() const
{
return mEngineInterface;
}
static OpenSLEngine OpenSLEngineInstance;
OpenSLEngine& OpenSLEngine::instance()
{
return OpenSLEngineInstance;
}
// --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId) {}
AndroidInputDevice::~AndroidInputDevice() {}
static int RateToProbe[12][2] = {
{SL_SAMPLINGRATE_16, 16000}, {SL_SAMPLINGRATE_8, 8000}, {SL_SAMPLINGRATE_32, 32000},
{SL_SAMPLINGRATE_44_1, 44100}, {SL_SAMPLINGRATE_11_025, 10025}, {SL_SAMPLINGRATE_22_05, 22050},
{SL_SAMPLINGRATE_24, 24000}, {SL_SAMPLINGRATE_48, 48000}, {SL_SAMPLINGRATE_64, 64000},
{SL_SAMPLINGRATE_88_2, 88200}, {SL_SAMPLINGRATE_96, 96000}, {SL_SAMPLINGRATE_192, 192000}};
bool AndroidInputDevice::open()
{
if (active())
return true;
OpenSLEngine::instance().open();
// Probe few sampling rates
bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]);
mDeviceRate = RateToProbe[rateIndex][1];
ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex);
opened = mDeviceRate != 0;
if (!opened)
internalClose();
}
catch (...)
{
opened = false;
internalClose();
}
}
mActive = opened;
return opened;
}
void AndroidInputDevice::close()
{
// There is no check for active() value because close() can be called to cleanup after bad open() call.
internalClose();
OpenSLEngine::instance().close();
mActive = false;
}
Format AndroidInputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidInputDevice::active() const
{
return mActive;
}
bool AndroidInputDevice::fakeMode()
{
return false;
}
void AndroidInputDevice::setFakeMode(bool fakemode) {}
int AndroidInputDevice::readBuffer(void* buffer)
{
std::unique_lock<std::mutex> l(mMutex);
while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE)
{
mDataCondVar.wait(l);
}
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
}
#define CHECK_SL_INTERFACE(INTF, ERR) \
{ \
if (!INTF) \
throw Exception(ERR_OPENSLES, ERR); \
if (!(*INTF)) \
throw Exception(ERR_OPENSLES, ERR); \
}
void AndroidInputDevice::internalOpen(int rateCode, int rate)
{
SLresult resultCode = 0;
SLuint32 nrOfChannels = 1;
// Prepare audio source
SLDataLocator_IODevice devDescription = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSource = {&devDescription, NULL};
// Source flags
SLuint32 speakersFlags =
nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Buffer queue
SLDataLocator_AndroidSimpleBufferQueue queueDescription = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
// Audio format
SLDataFormat_PCM formatDescription = {SL_DATAFORMAT_PCM, nrOfChannels,
(SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags,
SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSink = {&queueDescription, &formatDescription};
// Create recorder
// Do not forget about RECORD_AUDIO permission
const SLInterfaceID interfacesList[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
const SLboolean interfacesRequirements[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
// Get access to OpenSL engine
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
CHECK_SL_INTERFACE(engine_interface, -1);
resultCode = (*engine_interface)
->CreateAudioRecorder(OpenSLEngine::instance().getNativeEngine(), &mRecorderObject, &audioSource,
&audioSink, 2, interfacesList, interfacesRequirements);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderObject, -2);
// Obtain stream type
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg);
CHECK_OPENSLES_ERROR;
// Now audio recorder goes to real world
resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Get recorder interface
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderInterface, -3);
// Now buffer queue interface...
resultCode =
(*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
// Resampler is needed to provide SDK's rate
mResampler = std::make_shared<Resampler>();
mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE);
// Allocate recorder buffer size
mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2;
mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT);
mRecorderBufferIndex = 0;
// Setup data consuming callback
resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this);
CHECK_OPENSLES_ERROR;
// Setup buffers
for (int i = 0; i < AUDIO_MIC_BUFFER_COUNT; i++)
(*mRecorderBufferInterface)
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
// Start finally
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
CHECK_OPENSLES_ERROR;
}
void AndroidInputDevice::internalClose()
{
if (!mRecorderObject)
return;
if (*mRecorderObject)
{
if (active())
{
// Stop recording
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED);
// Wait until recording will not stop really
SLuint32 state = SL_RECORDSTATE_STOPPED;
do
{
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
SyncHelper::delay(1);
} while (state == SL_RECORDSTATE_RECORDING);
}
(*mRecorderObject)->Destroy(mRecorderObject);
}
mRecorderObject = nullptr;
mRecorderInterface = nullptr;
mRecorderBufferInterface = nullptr;
mAndroidCfg = nullptr;
}
void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair
if (mConnection)
mConnection->onMicData(getFormat(), mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
/*
// Send audio to cache with native sample rate
mDeviceRateCache.add(mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
// Check if there is enough data (10 ms) to send
int tenMsSize = (int)Format(mDeviceRate, 1).sizeFromTime(10);
while (mDeviceRateCache.filled() >= tenMsSize)
{
char* resampled = (char*)alloca(Format().sizeFromTime(10));
int processed = 0;
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled,
Format().sizeFromTime(10)); if (outlen > 0) mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
mDeviceRateCache.erase(tenMsSize);
}
// Tell about data
while (mSdkRateCache.filled() >= AUDIO_MIC_BUFFER_SIZE)
{
if (mConnection)
mConnection->onMicData(Format(), mSdkRateCache.data(), AUDIO_MIC_BUFFER_SIZE);
mSdkRateCache.erase(AUDIO_MIC_BUFFER_SIZE);
}
*/
// Re-enqueue used buffer
(*mRecorderBufferInterface)
->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
mRecorderBufferIndex++;
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
}
void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
try
{
if (context)
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
}
catch (...)
{
}
}
// ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId)
{
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << strx::toHex(this));
}
AndroidOutputDevice::~AndroidOutputDevice()
{
ICELogDebug(<< "Deleting AndroidOutputDevice.");
close();
}
bool AndroidOutputDevice::open()
{
std::unique_lock<std::mutex> l(mMutex);
bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true);
opened = true;
mDeviceRate = RateToProbe[rateIndex][1];
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
}
catch (...)
{
opened = false;
}
}
if (opened)
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
return opened;
}
void AndroidOutputDevice::close()
{
std::unique_lock<std::mutex> l(mMutex);
internalClose();
}
Format AndroidOutputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidOutputDevice::fakeMode()
{
return false;
}
void AndroidOutputDevice::setFakeMode(bool fakemode) {}
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
{
mInShutdown = false;
SLresult resultCode;
SLuint32 channels = 1;
// Configure audio source
SLDataLocator_AndroidSimpleBufferQueue queue_desc = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
const SLInterfaceID interfacesList[] = {SL_IID_VOLUME};
const SLboolean interfaceRequirements[] = {SL_BOOLEAN_FALSE};
resultCode = (*OpenSLEngine::instance().getNativeEngine())
->CreateOutputMix(OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
interfaceRequirements);
CHECK_OPENSLES_ERROR;
// Bring mixer online
resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Prepare mixer configuration
SLuint32 speakers = channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Describe audio format
SLDataFormat_PCM pcm_format = {
SL_DATAFORMAT_PCM, channels, (SLuint32)rateId, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, speakers, SL_BYTEORDER_LITTLEENDIAN};
// Describe audio source - buffers + audio format
SLDataSource audio_source = {&queue_desc, &pcm_format};
// Describe audio sink
SLDataLocator_OutputMix mixer_desc = {SL_DATALOCATOR_OUTPUTMIX, mMixer};
SLDataSink audio_sink = {&mixer_desc, NULL};
// Create player instance
const SLInterfaceID playerInterfaces[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME,
SL_IID_ANDROIDCONFIGURATION};
const SLboolean playerInterfacesReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
resultCode = (*OpenSLEngine::instance().getNativeEngine())
->CreateAudioPlayer(OpenSLEngine::instance().getNativeEngine(), &mPlayer, &audio_source,
&audio_sink, 3, playerInterfaces, playerInterfacesReqs);
CHECK_OPENSLES_ERROR;
// Get android config interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig);
if (resultCode == SL_RESULT_SUCCESS)
{
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
resultCode = (*mAndroidConfig)
->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
if (resultCode != SL_RESULT_SUCCESS)
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
}
else
ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode);
// Bring player online
resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Obtain player control
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl);
CHECK_OPENSLES_ERROR;
// Get the buffer queue interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mBufferQueue);
CHECK_OPENSLES_ERROR;
// Setup callback
resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this);
CHECK_OPENSLES_ERROR;
// Enqueue buffers
mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize);
mBufferIndex = 0;
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize, (SLuint32)mBufferSize);
// Set the player's state to playing
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
CHECK_OPENSLES_ERROR;
ICELogInfo(<< "Android audio output is opened and playing.");
}
void AndroidOutputDevice::internalClose()
{
if (mPlayer)
{
if (*mPlayer)
{
mInShutdown = true;
ICELogInfo(<< "Stop player");
if (mPlayerControl)
{
if (*mPlayerControl)
{
SLuint32 state = SL_PLAYSTATE_PLAYING;
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED)
{
(*mPlayerControl)->GetPlayState(mPlayerControl, &state);
SyncHelper::delay(1);
}
}
}
// Clear buffer queue
ICELogInfo(<< "Clear player buffer queue");
(*mBufferQueue)->Clear(mBufferQueue);
ICELogInfo(<< "Destroy player object");
// Destroy player object
(*mPlayer)->Destroy(mPlayer);
ICELogInfo(<< "Android audio output closed.");
mPlayer = nullptr;
mPlayerControl = nullptr;
mBufferQueue = nullptr;
mEffect = nullptr;
mAndroidConfig = nullptr;
}
}
if (mMixer)
{
if (*mMixer)
(*mMixer)->Destroy(mMixer);
mMixer = nullptr;
}
}
void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
if (mInShutdown)
return;
/*{
char silence[mBufferSize]; memset(silence, 0, mBufferSize);
(*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize);
return;
}*/
// Ask producer about data
char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize;
if (mConnection)
{
Format f = getFormat();
if (f.mRate != 0)
mConnection->onSpkData(f, buffer, mBufferSize);
}
(*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize);
mBufferIndex++;
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
}
void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
if (!context)
return;
try
{
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
}
catch (...)
{
}
}
#endif // TARGET_ANDROID