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