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
22static constexpr size_t OPENSLES_BUFFERS { 10 }; /* maximum number of buffers */
23static constexpr std::chrono::milliseconds OPENSLES_BUFLEN { 10ms };
24// static constexpr int32_t POSITIONUPDATEPERIOD { 1000 };
25// static constexpr int32_t POSITIONUPDATEPERIOD { 25000 };
26static constexpr int32_t POSITIONUPDATEPERIOD { 40 };
27// static constexpr int32_t 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 m_soHandle = dlopen("libOpenSLES.so", RTLD_NOW);
97 if (m_soHandle == nullptr)
98 {
99 LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Failed to load libOpenSLES");
100 Close();
101 return false;
102 }
103
105 if (m_slCreateEnginePtr == nullptr)
106 {
107 LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Failed to load symbol slCreateEngine");
108 Close();
109 return false;
110 }
111
112#define OPENSL_DLSYM(dest, name) \
113 do { \
114 const SLInterfaceID *sym = (const SLInterfaceID *)dlsym(m_soHandle, "SL_IID_" name); \
115 if (sym == nullptr) \
116 { \
117 LOG(VB_GENERAL, LOG_ERR, "AOOSLES Error: Failed to load symbol SL_IID_" name); \
118 Close(); \
119 return false; \
120 } \
121 (dest) = *sym; \
122 } while(0)
123
124 OPENSL_DLSYM(m_slIidAndroidSimpleBufferQueue, "ANDROIDSIMPLEBUFFERQUEUE");
125 OPENSL_DLSYM(m_slIidEngine, "ENGINE");
126 OPENSL_DLSYM(m_slIidPlay, "PLAY");
127 OPENSL_DLSYM(m_slIidVolume, "VOLUME");
128#undef OPENSL_DLSYM
129
130 // create engine
131 SLresult result = m_slCreateEnginePtr(&m_engineObject, 0, nullptr, 0, nullptr, nullptr);
132 CHECK_OPENSL_ERROR("Failed to create engine");
133
134 // realize the engine in synchronous mode
135 result = Realize(m_engineObject, SL_BOOLEAN_FALSE);
136 CHECK_OPENSL_ERROR("Failed to realize engine");
137
138 // get the engine interface, needed to create other objects
140 static_cast<void*>(&m_engineEngine));
141 CHECK_OPENSL_ERROR("Failed to get the engine interface");
142
143 // create output mix, with environmental reverb specified as a non-required interface
144 const std::array<SLInterfaceID,1> ids1 { m_slIidVolume };
145 const std::array<SLboolean,1> req1 { SL_BOOLEAN_FALSE };
147 1, ids1.data(), req1.data());
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 // configure audio source - this defines the number of samples you can enqueue.
160 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
161 SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
163 };
164
165 SLDataFormat_PCM format_pcm;
166 format_pcm.formatType = SL_DATAFORMAT_PCM;
167 format_pcm.numChannels = 2;
168 format_pcm.samplesPerSec = ((SLuint32) m_sampleRate * 1000) ;
169 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
170 switch (m_outputFormat)
171 {
172 case FORMAT_U8: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8; break;
173 case FORMAT_S16: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; break;
174#if 0
175 case FORMAT_S24LSB: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24; break;
176 case FORMAT_S24:
177 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
178 format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
179 break;
180 case FORMAT_S32: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; break;
181 // case FORMAT_FLT: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; break;
182#endif
183 default:
184 {
185 QString message {QCoreApplication::translate("AudioOutputOpenSLES", "Unknown sample format: %1")
186 .arg(m_outputFormat)};
187 dispatchError(message);
188 LOG(VB_GENERAL, LOG_ERR, message);
189 return false;
190 }
191 }
192 format_pcm.containerSize = format_pcm.bitsPerSample;
193 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
194
195 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
196
197 // configure audio sink
198 SLDataLocator_OutputMix loc_outmix = {
199 SL_DATALOCATOR_OUTPUTMIX,
201 };
202 SLDataSink audioSnk = {&loc_outmix, nullptr};
203
204 //create audio player
205 const std::array<SLInterfaceID,2> ids2 { m_slIidAndroidSimpleBufferQueue, m_slIidVolume };
206 static const std::array<SLboolean,2> req2 { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
207
208 SLresult result {0};
209 if (GetNativeOutputSampleRate() >= m_sampleRate) { // FIXME
210 result = CreateAudioPlayer(m_engineEngine, &m_playerObject, &audioSrc,
211 &audioSnk, ids2.size(), ids2.data(), req2.data());
212 } else {
213 // Don't try to play back a sample rate higher than the native one,
214 // since OpenSL ES will try to use the fast path, which AudioFlinger
215 // will reject (fast path can't do resampling), and will end up with
216 // too small buffers for the resampling. See http://b.android.com/59453
217 // for details. This bug is still present in 4.4. If it is fixed later
218 // this workaround could be made conditional.
219 result = SL_RESULT_UNKNOWN_ERROR;
220 }
221 if (result != SL_RESULT_SUCCESS) {
222 /* Try again with a more sensible samplerate */
223 //fmt->i_rate = 44100;
224 format_pcm.samplesPerSec = ((SLuint32) 48000 * 1000) ;
225 result = CreateAudioPlayer(m_engineEngine, &m_playerObject, &audioSrc,
226 &audioSnk, ids2.size(), ids2.data(), req2.data());
227 }
228 CHECK_OPENSL_ERROR("Failed to create audio player");
229
230 result = Realize(m_playerObject, SL_BOOLEAN_FALSE);
231 CHECK_OPENSL_ERROR("Failed to realize player object.");
232
234 static_cast<void*>(&m_playerPlay));
235 CHECK_OPENSL_ERROR("Failed to get player interface.");
236
238 static_cast<void*>(&m_volumeItf));
239 CHECK_OPENSL_ERROR("failed to get volume interface.");
240
242 static_cast<void*>(&m_playerBufferQueue));
243 CHECK_OPENSL_ERROR("Failed to get buff queue interface");
244
247 (void*)this);
248 CHECK_OPENSL_ERROR("Failed to register buff queue callback.");
249
250 // set the player's state to playing
251 result = SetPlayState(m_playerPlay, SL_PLAYSTATE_PLAYING);
252 CHECK_OPENSL_ERROR("Failed to switch to playing state");
253
254 /* XXX: rounding shouldn't affect us at normal sampling rate */
255 uint32_t samplesPerBuf = OPENSLES_BUFLEN.count() * m_sampleRate / 1000;
256 try {
257 m_buf.resize(OPENSLES_BUFFERS * samplesPerBuf * m_bytesPerFrame);
258 } catch (std::bad_alloc &ex) {
259 Stop();
260 return false;
261 }
262
263 m_started = false;
264 m_bufWriteIndex = 0;
265 m_bufWriteBase = 0;
266
267
268 // we want 16bit signed data native endian.
269 //fmt->i_format = VLC_CODEC_S16N;
270 //fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
271
272/* Buffers which arrive after pts - AOUT_MIN_PREPARE_TIME will be trashed
273 to avoid too heavy resampling */
274 //SetPositionUpdatePeriod(m_playerPlay, 25000);
275 //SetPositionUpdatePeriod(m_playerPlay, 40);
277
278 return true;
279}
280
282{
283 if (m_playerObject)
284 {
285 // set the player's state to playing
286 SLresult result = SetPlayState(m_playerPlay, SL_PLAYSTATE_STOPPED);
287 CHECK_OPENSL_ERROR("Failed to switch to not playing state");
289 m_playerObject = nullptr;
290 }
291 m_buf.clear();
292 m_buf.shrink_to_fit();
293 return true;
294}
295
297{
298 bool result = CreateEngine();
299 if (result)
300 {
301 result = StartPlayer();
302 }
303 if (!result)
304 {
305 Close();
306 }
307 return result;
308}
309
311{
312 Stop();
314 {
316 m_outputMixObject = nullptr;
317 }
318 if (m_engineObject)
319 {
321 m_engineObject = nullptr;
322 }
323 if (m_soHandle)
324 {
326 m_soHandle = nullptr;
327 }
328}
329
330
331
333 AudioOutputBase(settings),
334 m_nativeOutputSampleRate(GetNativeOutputSampleRate())
335{
336 InitSettings(settings);
337 if (settings.m_init)
338 Reconfigure(settings);
339}
340
342{
343 KillAudio();
345}
346
348{
349 auto *settings = new AudioOutputSettings();
350
351 int32_t nativeRate = GetNativeOutputSampleRate(); // in Hz
352 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Native Rate %1").arg(nativeRate));
353 //settings->AddSupportedRate(48000);
354 settings->AddSupportedRate(nativeRate);
355
356 // Support all standard formats
357 settings->AddSupportedFormat(FORMAT_U8);
358 settings->AddSupportedFormat(FORMAT_S16);
359 // settings->AddSupportedFormat(FORMAT_S24);
360 // settings->AddSupportedFormat(FORMAT_S32);
361#if 0 // 32-bit floating point (AC3) is not supported on all platforms.
362 settings->AddSupportedFormat(FORMAT_FLT);
363#endif
364
365 // Guess that we can do up to 2
366 settings->AddSupportedChannels(2);
367
368 settings->setPassthrough(0); //No passthrough
369
370 return settings;
371}
372
374{
375 CloseDevice();
376
377 if (!Open())
378 {
379 CloseDevice();
380 return false;
381 }
382
383 // fragments are 10ms worth of samples
385 // OpenSLES buffer holds 10 fragments = 80ms worth of samples
387
388 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Buffering %1 fragments of %2 bytes each, total: %3 bytes")
390
391 return true;
392}
393
395{
396 Stop();
397 Close();
398}
399
400void AudioOutputOpenSLES::SPlayedCallback(SLAndroidSimpleBufferQueueItf caller, void *pContext)
401{
402 ((AudioOutputOpenSLES*)pContext)->PlayedCallback(caller);
403}
404
405void AudioOutputOpenSLES::PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
406{
407 assert (caller == m_playerBufferQueue);
408
409 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Freed"));
410
411 m_lock.lock();
412 m_started = true;
413 m_lock.unlock();
414}
415
417{
418 SLAndroidSimpleBufferQueueState st;
419 SLresult res = GetState(m_playerBufferQueue, &st);
420 if (res != SL_RESULT_SUCCESS) {
421 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Could not query buffer queue state in %1 (%2)").arg(__func__).arg(res));
422 return -1;
423 }
424 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Num Queued %1").arg(st.count));
425 return st.count;
426}
427
428void AudioOutputOpenSLES::WriteAudio(unsigned char * aubuf, int size)
429{
430 LOG(VB_AUDIO, LOG_INFO, LOC + QString("WriteAudio %1").arg(size));
431 while (size > 0)
432 {
433 // check if there are any buffers available
434 int32_t numBufferesQueued = GetNumberOfBuffersQueued();
435 if (numBufferesQueued < 0)
436 return;
437
438 if (numBufferesQueued == OPENSLES_BUFFERS)
439 {
440 // wait for a buffer period, should be clear next time around
441 std::this_thread::sleep_for(OPENSLES_BUFLEN);
442 continue;
443 }
444
445 if (size < (m_fragmentSize + m_bufWriteIndex))
446 {
447 memcpy(&m_buf[m_bufWriteBase + m_bufWriteIndex], aubuf, size);
448 // no more to do so exit now, dont have a full buffer
449 break;
450 }
451
454
456 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Enqueue %1").arg(m_bufWriteBase));
457
458 if (r != SL_RESULT_SUCCESS)
459 {
460 // should never happen so show a log
461 LOG(VB_GENERAL, LOG_ERR, LOC + QString("error %1 when writing %2 bytes %3")
462 .arg(r)
463 .arg(m_fragmentSize)
464 .arg((r == SL_RESULT_BUFFER_INSUFFICIENT) ? " (buffer insufficient)" : ""));
465 // toss the remaining data, we got bigger problems
466 return;
467 }
468 // update pointers for next time only if the data was queued correctly
471 m_bufWriteBase = 0;
472 m_bufWriteIndex = 0;
473 }
474}
475
477{
478 int numBufferesQueued = GetNumberOfBuffersQueued();
479 if (numBufferesQueued < 0)
480 {
481 return 0;
482 }
483 return (numBufferesQueued * m_fragmentSize) + m_bufWriteIndex;
484}
485
487{
488 SLmillibel mb = 0;
489 int volume = 0;
490 SLresult r = GetVolumeLevel(m_volumeItf, &mb);
491 if (r == SL_RESULT_SUCCESS)
492 {
493 if (mb <= SL_MILLIBEL_MIN)
494 {
495 volume = 0;
496 }
497 else
498 {
499 volume = lroundf(expf(mb / (3*2000.0F)) * 100);
500 }
501 LOG(VB_AUDIO, LOG_INFO, LOC + QString("GetVolume(%1) %2 (%3)")
502 .arg(channel).arg(volume).arg(mb));
503 }
504 else
505 {
506 LOG(VB_GENERAL, LOG_ERR, LOC + QString("GetVolume(%1) %2 (%3) : %4")
507 .arg(channel).arg(volume).arg(mb).arg(r));
508 }
509
510 return volume;
511}
512
513void AudioOutputOpenSLES::SetVolumeChannel(int channel, int volume)
514{
515 if (channel > 1)
516 LOG(VB_GENERAL, LOG_ERR, LOC + "Android volume only supports stereo!");
517
518 // Volume is 0-100
519 // android expects 0-1.0 before conversion
520 // Convert UI volume to linear factor (cube) in log
521 float vol = volume / 100.0F;
522
523 int mb = 0;
524 if (volume == 0)
525 {
526 mb = SL_MILLIBEL_MIN;
527 }
528 else
529 {
530 // millibels from linear amplification
531 mb = lroundf(3 * 2000.F * log10f(vol));
532 if (mb < SL_MILLIBEL_MIN)
533 mb = SL_MILLIBEL_MIN;
534 else if (mb > 0)
535 mb = 0; // maximum supported level could be higher: GetMaxVolumeLevel */
536 }
537
538 SLresult r = SetVolumeLevel(m_volumeItf, mb);
539 if (r == SL_RESULT_SUCCESS)
540 {
541 LOG(VB_AUDIO, LOG_INFO, LOC + QString("SetVolume(%1) %2(%3)")
542 .arg(channel).arg(volume).arg(mb));
543 }
544 else
545 {
546 LOG(VB_GENERAL, LOG_ERR, LOC + QString("SetVolume(%1) %2(%3) : %4")
547 .arg(channel).arg(volume).arg(mb).arg(r));
548 }
549
550}
#define assert(x)
#define QAndroidJniEnvironment
#define LOC
int GetNativeOutputSampleRate(void)
#define SetPositionUpdatePeriod(a, b)
static constexpr size_t OPENSLES_BUFFERS
#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)
static constexpr int32_t POSITIONUPDATEPERIOD
#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 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)
std::vector< uint8_t > m_buf
void SetVolumeChannel(int channel, int volume) override
void WriteAudio(unsigned char *aubuf, int size) override
slCreateEngine_t m_slCreateEnginePtr
void PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
AudioOutputSettings * GetOutputSettings(bool digital) override
AudioOutputOpenSLES(const AudioSettings &settings)
SLInterfaceID m_slIidEngine
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_slIidVolume
SLInterfaceID m_slIidAndroidSimpleBufferQueue
int GetVolumeChannel(int channel) const override
bool OpenDevice(void) override
void CloseDevice(void) override
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:96
#define dlsym(x, y)
Definition: compat.h:97
#define dlopen(x, y)
Definition: compat.h:95
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39