MythTV master
audiooutputopensles.cpp
Go to the documentation of this file.
1#include <iostream>
2#include <thread>
3
4#include <dlfcn.h>
5
6#include <QtGlobal>
7#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
8#include <QAndroidJniEnvironment>
9#else
10#include <QJniEnvironment>
11#include <QJniObject>
12#define QAndroidJniEnvironment QJniEnvironment
13#define QAndroidJniObject QJniObject
14#endif
15
17#include "audiooutputopensles.h"
18
19
20#define LOC QString("AOSLES: ")
21
22#define OPENSLES_BUFFERS 10 /* maximum number of buffers */
23static constexpr std::chrono::milliseconds OPENSLES_BUFLEN { 10ms };
24//#define POSITIONUPDATEPERIOD 1000
25//#define POSITIONUPDATEPERIOD 25000
26#define POSITIONUPDATEPERIOD 40
27//#define POSITIONUPDATEPERIOD 200000
28
29#define CHECK_OPENSL_ERROR(msg) \
30 if (result != SL_RESULT_SUCCESS) \
31 { \
32 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Open Error: (%1)").arg(result)); \
33 Close(); \
34 return false; \
35 }
36
37#define CHECK_OPENSL_START_ERROR(msg) \
38 if (result != SL_RESULT_SUCCESS) \
39 { \
40 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start Error: (%1)").arg(result)); \
41 Stop(); \
42 return false; \
43 }
44
45
46#define Destroy(a) (*(a))->Destroy(a);
47#define SetPlayState(a, b) (*(a))->SetPlayState(a, b)
48#define RegisterCallback(a, b, c) (*(a))->RegisterCallback(a, b, c)
49#define GetInterface(a, b, c) (*(a))->GetInterface(a, b, c)
50#define Realize(a, b) (*(a))->Realize(a, b)
51#define CreateOutputMix(a, b, c, d, e) (*(a))->CreateOutputMix(a, b, c, d, e)
52#define CreateAudioPlayer(a, b, c, d, e, f, g) \
53 (*(a))->CreateAudioPlayer(a, b, c, d, e, f, g)
54#define Enqueue(a, b, c) (*(a))->Enqueue(a, b, c)
55#define Clear(a) (*(a))->Clear(a)
56#define GetState(a, b) (*(a))->GetState(a, b)
57#define SetPositionUpdatePeriod(a, b) (*(a))->SetPositionUpdatePeriod(a, b)
58#define SetVolumeLevel(a, b) (*(a))->SetVolumeLevel(a, b)
59#define GetVolumeLevel(a, b) (*(a))->GetVolumeLevel(a, b)
60#define SetMute(a, b) (*(a))->SetMute(a, b)
61
63
65{
66#if 1
68 jclass cls = jniEnv->FindClass("android/media/AudioTrack");
69 jmethodID method = jniEnv->GetStaticMethodID (cls, "getNativeOutputSampleRate", "(I)I");
70 int sample_rate = jniEnv->CallStaticIntMethod (cls, method, 3); // AudioManager.STREAM_MUSIC
71 return sample_rate;
72#else
73 //AudioManager.getProperty(PROPERTY_OUTPUT_SAMPLE_RATE)
74 return QAndroidJniObject::callStaticMethodInt("android/media/AudioTrack", "getNativeOutputSampleRate", "(I)I", 3);
75#endif
76}
77
78#if 0
79int GetNativeOutputFramesPerBuffer(void)
80{
81#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
82 QAndroidJniObject activity = QtAndroid::androidActivity();
83#else
84 QJniObject activity = QNativeInterface::QAndroidApplication::context();
85#endif
86 //Context.getSystemService(Context.AUDIO_SERVICE)
87 QAndroidJniObject audioManager = activity.callObjectMethod("getSystemService", "", );
88 jstring sValue = audioManager.callMethod<jstring>("getProperty", PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
89 // getNativeOutputFramesPerBuffer
90 return QAndroidJniObject::callStaticMethodInt("android/media/AudioManager", "getProperty", "(I)I", 3);
91}
92#endif
93
95{
96 SLresult result;
97
98 m_so_handle = dlopen("libOpenSLES.so", RTLD_NOW);
99 if (m_so_handle == nullptr)
100 {
101 LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Failed to load libOpenSLES");
102 Close();
103 return false;
104 }
105
107 if (m_slCreateEnginePtr == nullptr)
108 {
109 LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Failed to load symbol slCreateEngine");
110 Close();
111 return false;
112 }
113
114#define OPENSL_DLSYM(dest, name) \
115 do { \
116 const SLInterfaceID *sym = (const SLInterfaceID *)dlsym(m_so_handle, "SL_IID_" name); \
117 if (sym == nullptr) \
118 { \
119 LOG(VB_GENERAL, LOG_ERR, "AOOSLES Error: Failed to load symbol SL_IID_" name); \
120 Close(); \
121 return false; \
122 } \
123 (dest) = *sym; \
124 } while(0)
125
126 OPENSL_DLSYM(m_SL_IID_ANDROIDSIMPLEBUFFERQUEUE, "ANDROIDSIMPLEBUFFERQUEUE");
127 OPENSL_DLSYM(m_SL_IID_ENGINE, "ENGINE");
129 OPENSL_DLSYM(m_SL_IID_VOLUME, "VOLUME");
130#undef OPENSL_DLSYM
131
132 // create engine
133 result = m_slCreateEnginePtr(&m_engineObject, 0, nullptr, 0, nullptr, nullptr);
134 CHECK_OPENSL_ERROR("Failed to create engine");
135
136 // realize the engine in synchronous mode
137 result = Realize(m_engineObject, SL_BOOLEAN_FALSE);
138 CHECK_OPENSL_ERROR("Failed to realize engine");
139
140 // get the engine interface, needed to create other objects
142 CHECK_OPENSL_ERROR("Failed to get the engine interface");
143
144 // create output mix, with environmental reverb specified as a non-required interface
145 const SLInterfaceID ids1[] = { m_SL_IID_VOLUME };
146 const SLboolean req1[] = { SL_BOOLEAN_FALSE };
147 result = CreateOutputMix(m_engineEngine, &m_outputMixObject, 1, ids1, req1);
148 CHECK_OPENSL_ERROR("Failed to create output mix");
149
150 // realize the output mix in synchronous mode
151 result = Realize(m_outputMixObject, SL_BOOLEAN_FALSE);
152 CHECK_OPENSL_ERROR("Failed to realize output mix");
153
154 return true;
155}
156
158{
159 SLresult result;
160
161 // configure audio source - this defines the number of samples you can enqueue.
162 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
163 SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
165 };
166
167 SLDataFormat_PCM format_pcm;
168 format_pcm.formatType = SL_DATAFORMAT_PCM;
169 format_pcm.numChannels = 2;
170 format_pcm.samplesPerSec = ((SLuint32) m_sampleRate * 1000) ;
171 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
172 switch (m_outputFormat)
173 {
174 case FORMAT_U8: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8; break;
175 case FORMAT_S16: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; break;
176#if 0
177 case FORMAT_S24LSB: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24; break;
178 case FORMAT_S24:
179 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
180 format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
181 break;
182 case FORMAT_S32: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; break;
183 // case FORMAT_FLT: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; break;
184#endif
185 default:
186 {
187 QString message {QCoreApplication::translate("AudioOutputOpenSLES", "Unknown sample format: %1")
188 .arg(m_outputFormat)};
189 dispatchError(message);
190 LOG(VB_GENERAL, LOG_ERR, message);
191 return false;
192 }
193 }
194 format_pcm.containerSize = format_pcm.bitsPerSample;
195 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
196
197 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
198
199 // configure audio sink
200 SLDataLocator_OutputMix loc_outmix = {
201 SL_DATALOCATOR_OUTPUTMIX,
203 };
204 SLDataSink audioSnk = {&loc_outmix, nullptr};
205
206 //create audio player
207 const SLInterfaceID ids2[] = { m_SL_IID_ANDROIDSIMPLEBUFFERQUEUE, m_SL_IID_VOLUME };
208 static const SLboolean req2[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
209
210 if (GetNativeOutputSampleRate() >= m_sampleRate) { // FIXME
211 result = CreateAudioPlayer(m_engineEngine, &m_playerObject, &audioSrc,
212 &audioSnk, sizeof(ids2) / sizeof(*ids2),
213 ids2, req2);
214 } else {
215 // Don't try to play back a sample rate higher than the native one,
216 // since OpenSL ES will try to use the fast path, which AudioFlinger
217 // will reject (fast path can't do resampling), and will end up with
218 // too small buffers for the resampling. See http://b.android.com/59453
219 // for details. This bug is still present in 4.4. If it is fixed later
220 // this workaround could be made conditional.
221 result = SL_RESULT_UNKNOWN_ERROR;
222 }
223 if (result != SL_RESULT_SUCCESS) {
224 /* Try again with a more sensible samplerate */
225 //fmt->i_rate = 44100;
226 format_pcm.samplesPerSec = ((SLuint32) 48000 * 1000) ;
227 result = CreateAudioPlayer(m_engineEngine, &m_playerObject, &audioSrc,
228 &audioSnk, sizeof(ids2) / sizeof(*ids2),
229 ids2, req2);
230 }
231 CHECK_OPENSL_ERROR("Failed to create audio player");
232
233 result = Realize(m_playerObject, SL_BOOLEAN_FALSE);
234 CHECK_OPENSL_ERROR("Failed to realize player object.");
235
237 CHECK_OPENSL_ERROR("Failed to get player interface.");
238
240 CHECK_OPENSL_ERROR("failed to get volume interface.");
241
244 CHECK_OPENSL_ERROR("Failed to get buff queue interface");
245
248 (void*)this);
249 CHECK_OPENSL_ERROR("Failed to register buff queue callback.");
250
251 // set the player's state to playing
252 result = SetPlayState(m_playerPlay, SL_PLAYSTATE_PLAYING);
253 CHECK_OPENSL_ERROR("Failed to switch to playing state");
254
255 /* XXX: rounding shouldn't affect us at normal sampling rate */
256 uint32_t samplesPerBuf = OPENSLES_BUFLEN.count() * m_sampleRate / 1000;
257 m_buf = (uint8_t*)malloc(OPENSLES_BUFFERS * samplesPerBuf * m_bytesPerFrame);
258 if (!m_buf)
259 {
260 Stop();
261 return false;
262 }
263
264 m_started = false;
265 m_bufWriteIndex = 0;
266 m_bufWriteBase = 0;
267
268
269 // we want 16bit signed data native endian.
270 //fmt->i_format = VLC_CODEC_S16N;
271 //fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
272
273/* Buffers which arrive after pts - AOUT_MIN_PREPARE_TIME will be trashed
274 to avoid too heavy resampling */
275 //SetPositionUpdatePeriod(m_playerPlay, 25000);
276 //SetPositionUpdatePeriod(m_playerPlay, 40);
278
279 return true;
280}
281
283{
284 SLresult result;
285 if (m_playerObject)
286 {
287 // set the player's state to playing
288 result = SetPlayState(m_playerPlay, SL_PLAYSTATE_STOPPED);
289 CHECK_OPENSL_ERROR("Failed to switch to not playing state");
291 m_playerObject = nullptr;
292 }
293 if (m_buf)
294 {
295 free(m_buf);
296 m_buf = nullptr;
297 }
298 return true;
299}
300
302{
303 bool result = CreateEngine();
304 if (result)
305 {
306 result = StartPlayer();
307 }
308 if (!result)
309 {
310 Close();
311 }
312 return result;
313}
314
316{
317 Stop();
319 {
321 m_outputMixObject = nullptr;
322 }
323 if (m_engineObject)
324 {
326 m_engineObject = nullptr;
327 }
328 if (m_so_handle)
329 {
331 m_so_handle = nullptr;
332 }
333}
334
335
336
338 AudioOutputBase(settings),
339 m_nativeOutputSampleRate(GetNativeOutputSampleRate())
340{
341 InitSettings(settings);
342 if (settings.m_init)
343 Reconfigure(settings);
344}
345
347{
348 KillAudio();
350}
351
353{
355
356 int32_t nativeRate = GetNativeOutputSampleRate(); // in Hz
357 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Native Rate %1").arg(nativeRate));
358 //settings->AddSupportedRate(48000);
359 settings->AddSupportedRate(nativeRate);
360
361 // Support all standard formats
362 settings->AddSupportedFormat(FORMAT_U8);
364 // settings->AddSupportedFormat(FORMAT_S24);
365 // settings->AddSupportedFormat(FORMAT_S32);
366#if 0 // 32-bit floating point (AC3) is not supported on all platforms.
368#endif
369
370 // Guess that we can do up to 2
371 settings->AddSupportedChannels(2);
372
373 settings->setPassthrough(0); //No passthrough
374
375 return settings;
376}
377
379{
380 CloseDevice();
381
382 if (!Open())
383 {
384 CloseDevice();
385 return false;
386 }
387
388 // fragments are 10ms worth of samples
390 // OpenSLES buffer holds 10 fragments = 80ms worth of samples
392
393 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Buffering %1 fragments of %2 bytes each, total: %3 bytes")
395
396 return true;
397}
398
400{
401 Stop();
402 Close();
403}
404
405void AudioOutputOpenSLES::SPlayedCallback(SLAndroidSimpleBufferQueueItf caller, void *pContext)
406{
407 ((AudioOutputOpenSLES*)pContext)->PlayedCallback(caller);
408}
409
410void AudioOutputOpenSLES::PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
411{
412 assert (caller == m_playerBufferQueue);
413
414 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Freed"));
415
416 m_lock.lock();
417 m_started = true;
418 m_lock.unlock();
419}
420
422{
423 SLAndroidSimpleBufferQueueState st;
424 SLresult res = GetState(m_playerBufferQueue, &st);
425 if (res != SL_RESULT_SUCCESS) {
426 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Could not query buffer queue state in %1 (%2)").arg(__func__).arg(res));
427 return -1;
428 }
429 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Num Queued %1").arg(st.count));
430 return st.count;
431}
432
433void AudioOutputOpenSLES::WriteAudio(unsigned char * buffer, int size)
434{
435 LOG(VB_AUDIO, LOG_INFO, LOC + QString("WriteAudio %1").arg(size));
436 while (size > 0)
437 {
438 // check if there are any buffers available
439 int32_t numBufferesQueued = GetNumberOfBuffersQueued();
440 if (numBufferesQueued < 0)
441 return;
442
443 if (numBufferesQueued == OPENSLES_BUFFERS)
444 {
445 // wait for a buffer period, should be clear next time around
446 std::this_thread::sleep_for(OPENSLES_BUFLEN);
447 continue;
448 }
449
450 if (size < (m_fragmentSize + m_bufWriteIndex))
451 {
452 memcpy(&m_buf[m_bufWriteBase + m_bufWriteIndex], buffer, size);
453 size = 0;
454 // no more to do so exit now, dont have a full buffer
455 break;
456 }
457 else
458 {
461 }
462
464 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Enqueue %1").arg(m_bufWriteBase));
465
466 if (r != SL_RESULT_SUCCESS)
467 {
468 // should never happen so show a log
469 LOG(VB_GENERAL, LOG_ERR, LOC + QString("error %1 when writing %2 bytes %3")
470 .arg(r)
471 .arg(m_fragmentSize)
472 .arg((r == SL_RESULT_BUFFER_INSUFFICIENT) ? " (buffer insufficient)" : ""));
473 // toss the remaining data, we got bigger problems
474 return;
475 }
476 // update pointers for next time only if the data was queued correctly
479 m_bufWriteBase = 0;
480 m_bufWriteIndex = 0;
481 }
482}
483
485{
486 int numBufferesQueued = GetNumberOfBuffersQueued();
487 if (numBufferesQueued < 0)
488 {
489 return 0;
490 }
491 return numBufferesQueued * m_fragmentSize + m_bufWriteIndex;
492}
493
495{
496 SLmillibel mb = 0;
497 int volume = 0;
498 SLresult r = GetVolumeLevel(m_volumeItf, &mb);
499 if (r == SL_RESULT_SUCCESS)
500 {
501 if (mb <= SL_MILLIBEL_MIN)
502 {
503 volume = 0;
504 }
505 else
506 {
507 volume = lroundf(expf(mb / (3*2000.0F)) * 100);
508 }
509 LOG(VB_AUDIO, LOG_INFO, LOC + QString("GetVolume(%1) %2 (%3)")
510 .arg(channel).arg(volume).arg(mb));
511 }
512 else
513 {
514 LOG(VB_GENERAL, LOG_ERR, LOC + QString("GetVolume(%1) %2 (%3) : %4")
515 .arg(channel).arg(volume).arg(mb).arg(r));
516 }
517
518 return volume;
519}
520
521void AudioOutputOpenSLES::SetVolumeChannel(int channel, int volume)
522{
523 if (channel > 1)
524 LOG(VB_GENERAL, LOG_ERR, LOC + "Android volume only supports stereo!");
525
526 // Volume is 0-100
527 // android expects 0-1.0 before conversion
528 // Convert UI volume to linear factor (cube) in log
529 float vol = volume / 100.f;
530
531 int mb;
532 if (volume == 0)
533 {
534 mb = SL_MILLIBEL_MIN;
535 }
536 else
537 {
538 // millibels from linear amplification
539 mb = lroundf(3 * 2000.F * log10f(vol));
540 if (mb < SL_MILLIBEL_MIN)
541 mb = SL_MILLIBEL_MIN;
542 else if (mb > 0)
543 mb = 0; // maximum supported level could be higher: GetMaxVolumeLevel */
544 }
545
546 SLresult r = SetVolumeLevel(m_volumeItf, mb);
547 if (r == SL_RESULT_SUCCESS)
548 {
549 LOG(VB_AUDIO, LOG_INFO, LOC + QString("SetVolume(%1) %2(%3)")
550 .arg(channel).arg(volume).arg(mb));
551 }
552 else
553 {
554 LOG(VB_GENERAL, LOG_ERR, LOC + QString("SetVolume(%1) %2(%3) : %4")
555 .arg(channel).arg(volume).arg(mb).arg(r));
556 }
557
558}
#define assert(x)
#define OPENSLES_BUFFERS
#define QAndroidJniEnvironment
#define LOC
int GetNativeOutputSampleRate(void)
#define SetPositionUpdatePeriod(a, b)
#define GetInterface(a, b, c)
#define CreateAudioPlayer(a, b, c, d, e, f, g)
#define Enqueue(a, b, c)
#define RegisterCallback(a, b, c)
#define Destroy(a)
#define Realize(a, b)
#define GetState(a, b)
#define GetVolumeLevel(a, b)
#define OPENSL_DLSYM(dest, name)
static constexpr std::chrono::milliseconds OPENSLES_BUFLEN
#define CHECK_OPENSL_ERROR(msg)
#define SetVolumeLevel(a, b)
#define POSITIONUPDATEPERIOD
#define QAndroidJniObject
#define SetPlayState(a, b)
#define CreateOutputMix(a, b, c, d, e)
SLresult(*)(SLObjectItf *, SLuint32, const SLEngineOption *, SLuint32, const SLInterfaceID *, const SLboolean *) slCreateEngine_t
@ FORMAT_U8
@ FORMAT_S32
@ FORMAT_S24
@ FORMAT_FLT
@ FORMAT_S16
@ FORMAT_S24LSB
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
AudioFormat m_outputFormat
void InitSettings(const AudioSettings &settings)
static void SPlayedCallback(SLAndroidSimpleBufferQueueItf caller, void *pContext)
void SetVolumeChannel(int channel, int volume) override
void WriteAudio(unsigned char *aubuf, int size) override
SLInterfaceID m_SL_IID_PLAY
slCreateEngine_t m_slCreateEnginePtr
SLInterfaceID m_SL_IID_VOLUME
void PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
AudioOutputSettings * GetOutputSettings(bool digital) override
AudioOutputOpenSLES(const AudioSettings &settings)
SLInterfaceID m_SL_IID_ANDROIDSIMPLEBUFFERQUEUE
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
int GetNumberOfBuffersQueued() const
SLAndroidSimpleBufferQueueItf m_playerBufferQueue
SLInterfaceID m_SL_IID_ENGINE
int GetVolumeChannel(int channel) const override
bool OpenDevice(void) override
void CloseDevice(void) override
void AddSupportedRate(int rate)
void setPassthrough(int val)
void AddSupportedChannels(int channels)
void AddSupportedFormat(AudioFormat format)
void dispatchError(const QString &e)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
#define dlclose(x)
Definition: compat.h:99
#define dlsym(x, y)
Definition: compat.h:100
#define dlopen(x, y)
Definition: compat.h:98
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39