MythTV  master
audiooutput_omx.cpp
Go to the documentation of this file.
1 #include "audiooutput_omx.h"
2 
3 #include <algorithm> // max/min
4 #include <cassert>
5 #include <cstddef>
6 #include <vector>
7 
8 #include <OMX_Core.h>
9 #include <OMX_Audio.h>
10 #ifdef USING_BROADCOM
11 #include <OMX_Broadcom.h>
12 #include <bcm_host.h>
13 #endif
14 
15 // MythTV
16 #include "mythcorecontext.h"
17 
18 #include "omxcontext.h"
19 using namespace omxcontext;
20 
21 
22 /*
23  * Macros
24  */
25 #define LOC QString("AOMX:%1 ").arg(m_audiorender.Id())
26 
27 // Stringize a macro
28 #define _STR(s) #s
29 #define STR(s) _STR(s)
30 #define CASE2STR(f) case f: return STR(f)
31 
32 // Component names
33 #ifdef USING_BELLAGIO
34 # define AUDIO_RENDER "alsasink"
35 # define AUDIO_DECODE "audio_decoder.mp3.mad" // or audio_decoder.ogg.single
36 #else
37 # define AUDIO_RENDER "audio_render"
38 # define AUDIO_DECODE "audio_decode"
39 #endif
40 
41 
42 /*
43  * Types
44  */
45 
46 
47 /*
48  * Functions
49  */
51  AudioOutputBase(settings),
52  m_audiorender(gCoreContext->GetSetting("OMXAudioRender", AUDIO_RENDER), *this)
53 {
54  if (m_audiorender.GetState() != OMX_StateLoaded)
55  return;
56 
57  if (getenv("NO_OPENMAX_AUDIO"))
58  {
59  LOG(VB_AUDIO, LOG_NOTICE, LOC + "OpenMAX audio disabled");
60  return;
61  }
62 
63 #ifndef USING_BELLAGIO // Bellagio 0.9.3 times out in PortDisable
64  // Disable the renderer's clock port, otherwise can't goto OMX_StateIdle
65  if (OMX_ErrorNone == m_audiorender.Init(OMX_IndexParamOtherInit))
66  m_audiorender.PortDisable(0, 500);
67 #endif
68 
69  if (OMX_ErrorNone != m_audiorender.Init(OMX_IndexParamAudioInit))
70  return;
71 
72  if (!m_audiorender.IsValid())
73  return;
74 
75  // Show default port definitions and audio formats supported
76  for (unsigned port = 0; port < m_audiorender.Ports(); ++port)
77  {
78  m_audiorender.ShowPortDef(port, LOG_DEBUG, VB_AUDIO);
79 #if 0
80  m_audiorender.ShowFormats(port, LOG_DEBUG, VB_AUDIO);
81 #endif
82  }
83 
84  InitSettings(settings);
85  if (settings.m_init)
86  Reconfigure(settings);
87 }
88 
89 // virtual
91 {
92  KillAudio();
93 
94  // Must shutdown the OMX components now before our state becomes invalid.
95  // When the component's dtor is called our state has already been destroyed.
97 }
98 
99 template<typename T>
100 static inline void SetupChannels(T &t)
101 {
102  for (int i = OMX_AUDIO_MAXCHANNELS - 1; i >= int(t.nChannels); --i)
103  t.eChannelMapping[i] = OMX_AUDIO_ChannelNone;
104  switch (t.nChannels)
105  {
106  default:
107  case 8:
108  t.eChannelMapping[7] = OMX_AUDIO_ChannelRS;
109  [[clang::fallthrough]];
110  case 7:
111  t.eChannelMapping[6] = OMX_AUDIO_ChannelLS;
112  [[clang::fallthrough]];
113  case 6:
114  t.eChannelMapping[5] = OMX_AUDIO_ChannelRR;
115  [[clang::fallthrough]];
116  case 5:
117  t.eChannelMapping[4] = OMX_AUDIO_ChannelLR;
118  [[clang::fallthrough]];
119  case 4:
120  t.eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
121  [[clang::fallthrough]];
122  case 3:
123  t.eChannelMapping[2] = OMX_AUDIO_ChannelCF;
124  [[clang::fallthrough]];
125  case 2:
126  t.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
127  t.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
128  break;
129  case 1:
130  t.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
131  break;
132  case 0:
133  break;
134  }
135 }
136 
137 static bool Format2Pcm(OMX_AUDIO_PARAM_PCMMODETYPE &pcm, AudioFormat afmt)
138 {
139  switch (afmt)
140  {
141  case FORMAT_U8:
142  pcm.eNumData = OMX_NumericalDataUnsigned;
143  pcm.eEndian = OMX_EndianLittle;
144  pcm.nBitPerSample = 8;
145  break;
146  case FORMAT_S16:
147  pcm.eNumData = OMX_NumericalDataSigned;
148  pcm.eEndian = OMX_EndianLittle;
149  pcm.nBitPerSample = 16;
150  break;
151  case FORMAT_S24LSB:
152  pcm.eNumData = OMX_NumericalDataSigned;
153  pcm.eEndian = OMX_EndianLittle;
154  pcm.nBitPerSample = 24;
155  break;
156  case FORMAT_S24:
157  pcm.eNumData = OMX_NumericalDataSigned;
158  pcm.eEndian = OMX_EndianBig;
159  pcm.nBitPerSample = 24;
160  break;
161  case FORMAT_S32:
162  pcm.eNumData = OMX_NumericalDataSigned;
163  pcm.eEndian = OMX_EndianLittle;
164  pcm.nBitPerSample = 32;
165  break;
166  case FORMAT_FLT:
167  default:
168  return false;
169  }
170  pcm.bInterleaved = OMX_TRUE;
171  pcm.ePCMMode = OMX_AUDIO_PCMModeLinear;
172  return true;
173 }
174 
175 #ifdef OMX_AUDIO_CodingDDP_Supported
176 static const char *toString(OMX_AUDIO_DDPBITSTREAMID id)
177 {
178  switch (id)
179  {
180  CASE2STR(OMX_AUDIO_DDPBitStreamIdAC3);
181  CASE2STR(OMX_AUDIO_DDPBitStreamIdEAC3);
182  default: break;
183  }
184  static char buf[32];
185  return strcpy(buf, qPrintable(QString("DDPBitStreamId 0x%1").arg(id,0,16)));
186 }
187 #endif
188 
189 // virtual
191 {
192  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " begin");
193 
194  if (!m_audiorender.IsValid())
195  {
196  LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + " No audio render");
197  return false;
198  }
199 
201 
202  OMX_ERRORTYPE e = OMX_ErrorNone;
203  unsigned nBitPerSample = 0;
204 
205  OMX_AUDIO_PARAM_PORTFORMATTYPE fmt;
206  OMX_DATA_INIT(fmt);
207  fmt.nPortIndex = m_audiorender.Base();
208 
209  if (m_passthru || m_enc)
210  {
211  switch (m_codec)
212  {
213 #ifdef OMX_AUDIO_CodingDDP_Supported
214  case AV_CODEC_ID_AC3:
215  case AV_CODEC_ID_EAC3:
216  nBitPerSample = 16;
217  fmt.eEncoding = OMX_AUDIO_CodingDDP;
218  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
219  if (e != OMX_ErrorNone)
220  {
221  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
222  "SetParameter AudioPortFormat DDP error %1")
223  .arg(Error2String(e)));
224  return false;
225  }
226 
227  OMX_AUDIO_PARAM_DDPTYPE ddp;
228  OMX_DATA_INIT(ddp);
229  ddp.nPortIndex = m_audiorender.Base();
230  e = m_audiorender.GetParameter(OMX_IndexParamAudioDdp, &ddp);
231  if (e != OMX_ErrorNone)
232  {
233  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
234  "GetParameter AudioDdp error %1")
235  .arg(Error2String(e)));
236  return false;
237  }
238 
239  ddp.nChannels = m_channels;
240  ddp.nBitRate = 0;
241  ddp.nSampleRate = m_samplerate;
242  ddp.eBitStreamId = (AV_CODEC_ID_AC3 == m_codec) ?
243  OMX_AUDIO_DDPBitStreamIdAC3 :
244  OMX_AUDIO_DDPBitStreamIdEAC3;
245  ddp.eBitStreamMode = OMX_AUDIO_DDPBitStreamModeCM;
246  ddp.eDolbySurroundMode = OMX_AUDIO_DDPDolbySurroundModeNotIndicated;
247  ::SetupChannels(ddp);
248 
249  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + QString(
250  " DDP %1 chnls @ %2 sps %3 bps %4")
251  .arg(ddp.nChannels).arg(ddp.nSampleRate).arg(ddp.nBitRate)
252  .arg(toString(ddp.eBitStreamId)) );
253 
254  e = m_audiorender.SetParameter(OMX_IndexParamAudioDdp, &ddp);
255  if (e != OMX_ErrorNone)
256  {
257  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
258  "SetParameter AudioDdp error %1")
259  .arg(Error2String(e)));
260  return false;
261  }
262  break;
263 #endif //def OMX_AUDIO_CodingDDP_Supported
264 
265 #ifdef OMX_AUDIO_CodingDTS_Supported
266  case AV_CODEC_ID_DTS:
267  nBitPerSample = 16;
268  fmt.eEncoding = OMX_AUDIO_CodingDTS;
269  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
270  if (e != OMX_ErrorNone)
271  {
272  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
273  "SetParameter AudioPortFormat DTS error %1")
274  .arg(Error2String(e)));
275  return false;
276  }
277 
278  OMX_AUDIO_PARAM_DTSTYPE dts;
279  OMX_DATA_INIT(dts);
280  dts.nPortIndex = m_audiorender.Base();
281  e = m_audiorender.GetParameter(OMX_IndexParamAudioDts, &dts);
282  if (e != OMX_ErrorNone)
283  {
284  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
285  "GetParameter AudioDts error %1")
286  .arg(Error2String(e)));
287  return false;
288  }
289 
290  dts.nChannels = m_channels;
291  dts.nBitRate = 0;
292  dts.nSampleRate = m_samplerate;
293  // TODO
294  //dts.nDtsType; // OMX_U32 DTS type 1, 2, or 3
295  //dts.nFormat; // OMX_U32 DTS stream is either big/little endian and 16/14 bit packing
296  //dts.nDtsFrameSizeBytes; // OMX_U32 DTS frame size in bytes
297  ::SetupChannels(dts);
298 
299  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + QString(
300  " DTS %1 chnls @ %2 sps")
301  .arg(dts.nChannels).arg(dts.nSampleRate) );
302 
303  e = m_audiorender.SetParameter(OMX_IndexParamAudioDts, &dts);
304  if (e != OMX_ErrorNone)
305  {
306  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
307  "SetParameter AudioDts error %1")
308  .arg(Error2String(e)));
309  return false;
310  }
311  break;
312 #endif //def OMX_AUDIO_CodingDTS_Supported
313 
314  default:
315  LOG(VB_AUDIO, LOG_NOTICE, LOC + __func__ +
316  QString(" codec %1 not supported")
317  .arg(ff_codec_id_string(AVCodecID(m_codec))));
318  return false;
319  }
320  }
321  else
322  {
323  fmt.eEncoding = OMX_AUDIO_CodingPCM;
324  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
325  if (e != OMX_ErrorNone)
326  {
327  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
328  "SetParameter AudioPortFormat PCM error %1")
329  .arg(Error2String(e)));
330  return false;
331  }
332 
333  OMX_AUDIO_PARAM_PCMMODETYPE pcm;
334  OMX_DATA_INIT(pcm);
335  pcm.nPortIndex = m_audiorender.Base();
336  e = m_audiorender.GetParameter(OMX_IndexParamAudioPcm, &pcm);
337  if (e != OMX_ErrorNone)
338  {
339  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
340  "GetParameter AudioPcm error %1")
341  .arg(Error2String(e)));
342  return false;
343  }
344 
345  pcm.nChannels = m_channels;
346  pcm.nSamplingRate = m_samplerate;
347  if (!::Format2Pcm(pcm, m_output_format))
348  {
349  LOG(VB_AUDIO, LOG_ERR, LOC + __func__ + QString(
350  " Unsupported format %1")
352  return false;
353  }
354 
355  ::SetupChannels(pcm);
356 
357  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + QString(
358  " PCM %1 chnls @ %2 sps %3 %4 bits")
359  .arg(pcm.nChannels).arg(pcm.nSamplingRate).arg(pcm.nBitPerSample)
360  .arg(pcm.eNumData == OMX_NumericalDataSigned ? "signed" : "unsigned") );
361 
362  e = m_audiorender.SetParameter(OMX_IndexParamAudioPcm, &pcm);
363  if (e != OMX_ErrorNone)
364  {
365  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
366  "SetParameter AudioPcm error %1")
367  .arg(Error2String(e)));
368  return false;
369  }
370 
371  nBitPerSample = pcm.nBitPerSample;
372  }
373 
374  // Setup buffer size & count
375  // NB the OpenMAX spec requires PCM buffer size >= 5mS data
377  OMX_PARAM_PORTDEFINITIONTYPE &def = m_audiorender.PortDef();
378  def.nBufferSize = std::max(
379  OMX_U32((1024 * nBitPerSample * m_channels) / 8),
380  def.nBufferSize);
381  def.nBufferCountActual = std::max(OMX_U32(10), def.nBufferCountActual);
382  //def.bBuffersContiguous = OMX_FALSE;
383  def.nBufferAlignment = std::max(OMX_U32(sizeof(int)), def.nBufferAlignment);
384  e = m_audiorender.SetParameter(OMX_IndexParamPortDefinition, &def);
385  if (e != OMX_ErrorNone)
386  {
387  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
388  "SetParameter PortDefinition error %1")
389  .arg(Error2String(e)));
390  return false;
391  }
392 
393  // set AudioOutputBase member variables
394  m_soundcard_buffer_size = def.nBufferSize * def.nBufferCountActual;
395  m_fragment_size = def.nBufferSize;
396 
397 #ifdef USING_BROADCOM
398  // Select output device
399  QString dev;
400  if (m_passthru)
401  dev = "hdmi";
402  else if (m_main_device.contains(":hdmi"))
403  dev = "hdmi";
404  else
405  dev = "local";
406  LOG(VB_AUDIO, LOG_INFO, LOC + QString("Output device: '%1'").arg(dev));
407 
408  OMX_CONFIG_BRCMAUDIODESTINATIONTYPE dest;
410  strncpy((char*)dest.sName, dev.toLatin1().constData(), sizeof dest.sName);
411  e = m_audiorender.SetConfig(OMX_IndexConfigBrcmAudioDestination, &dest);
412  if (e != OMX_ErrorNone)
413  {
414  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
415  "SetConfig BrcmAudioDestination '%1' error %2")
416  .arg(dev).arg(Error2String(e)));
417  return false;
418  }
419 #endif
420 
421  // Goto OMX_StateIdle & allocate buffers
423  e = m_audiorender.SetState(OMX_StateIdle, 500, &cb);
424  if (e != OMX_ErrorNone)
425  {
426  // DDP/DTS mode returns OMX_ErrorInsufficientResources if o/p != hdmi
427  return false;
428  }
429 
430  // Goto OMX_StateExecuting
431  e = m_audiorender.SetState(OMX_StateExecuting, 500);
432  if (e != OMX_ErrorNone)
433  return false;
434 
435  if (internal_vol && !OpenMixer())
436  LOG(VB_GENERAL, LOG_ERR, LOC + __func__ +
437  " Unable to open audio mixer. Volume control disabled");
438 
439  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " end");
440  return true;
441 }
442 
443 // virtual
445 {
446  LOG(VB_AUDIO, LOG_INFO, LOC + __func__);
448 }
449 
450 // HDMI uses a different channel order from WAV and others
451 // See CEA spec: Table 20, Audio InfoFrame
452 
453 #define REORD_NUMCHAN 6 // Min Num of channels for reorder to be done
454 #define REORD_A 2 // First channel to switch
455 #define REORD_B 3 // Second channel to switch
456 
457 void AudioOutputOMX::reorderChannels(int *aubuf, int size)
458 {
459  int t_size = size;
460  int *sample = aubuf;
461  while (t_size >= REORD_NUMCHAN*4)
462  {
463  int savefirst = sample[REORD_A];
464  sample[REORD_A] = sample[REORD_B];
465  sample[REORD_B] = savefirst;
466  sample += m_channels;
467  t_size -= m_output_bytes_per_frame;
468  }
469 }
470 
471 void AudioOutputOMX::reorderChannels(short *aubuf, int size)
472 {
473  int t_size = size;
474  short *sample = aubuf;
475  while (t_size >= REORD_NUMCHAN*2)
476  {
477  short savefirst = sample[REORD_A];
478  sample[REORD_A] = sample[REORD_B];
479  sample[REORD_B] = savefirst;
480  sample += m_channels;
481  t_size -= m_output_bytes_per_frame;
482  }
483 }
484 
485 void AudioOutputOMX::reorderChannels(uchar *aubuf, int size)
486 {
487  int t_size = size;
488  uchar *sample = aubuf;
489  while (t_size >= REORD_NUMCHAN)
490  {
491  uchar savefirst = sample[REORD_A];
492  sample[REORD_A] = sample[REORD_B];
493  sample[REORD_B] = savefirst;
494  sample += m_channels;
495  t_size -= m_output_bytes_per_frame;
496  }
497 }
498 
499 // virtual
500 void AudioOutputOMX::WriteAudio(uchar *aubuf, int size)
501 {
502  if (!m_audiorender.IsValid())
503  {
504  LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + " No audio render");
505  return;
506  }
507 
508  // Reorder channels for CEA format
509  // See CEA spec: Table 20, Audio InfoFrame
510  if (!m_enc && !m_reenc && m_channels >= REORD_NUMCHAN)
511  {
512  int samplesize = m_output_bytes_per_frame / m_channels;
513  switch (samplesize)
514  {
515  case 1:
516  reorderChannels(aubuf, size);
517  break;
518  case 2:
519  reorderChannels((short*)aubuf, size);
520  break;
521  case 4:
522  reorderChannels((int*)aubuf, size);
523  break;
524  }
525  }
526 
527  while (size > 0)
528  {
529  if (!m_ibufs_sema.tryAcquire(1, 3000))
530  {
531  LOG(VB_AUDIO, LOG_WARNING, LOC + __func__ + " - no input buffers");
532  break;
533  }
534  m_lock.lock();
535  assert(!m_ibufs.isEmpty());
536  OMX_BUFFERHEADERTYPE *hdr = m_ibufs.takeFirst();
537  m_lock.unlock();
538 
539  int free = int(hdr->nAllocLen) - int(hdr->nFilledLen + hdr->nOffset);
540  int cnt = (free > size) ? size : free;
541  memcpy(&hdr->pBuffer[hdr->nOffset + hdr->nFilledLen], aubuf, cnt);
542  hdr->nFilledLen += cnt;
543  aubuf += cnt;
544  size -= cnt;
545  m_pending.fetchAndAddRelaxed(cnt);
546 
547  hdr->nTimeStamp = S64_TO_TICKS(0);
548  hdr->nFlags = 0;
549 
550  OMX_ERRORTYPE e = OMX_EmptyThisBuffer(m_audiorender.Handle(), hdr);
551  if (e != OMX_ErrorNone)
552  {
553  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
554  "OMX_EmptyThisBuffer error %1").arg(Error2String(e)) );
555  m_pending.fetchAndAddRelaxed(-cnt);
556  m_lock.lock();
557  m_ibufs.append(hdr);
558  m_lock.unlock();
559  m_ibufs_sema.release();
560  break;
561  }
562  }
563 }
564 
569 // virtual
571 {
572  if (!m_audiorender.IsValid())
573  {
574  LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + " No audio render");
575  return 0;
576  }
577 
578 #ifdef USING_BROADCOM
579  // output bits per 10 frames
580  int obpf;
581  if (m_passthru && !usesSpdif())
583  else
584  obpf = m_output_bytes_per_frame * 80;
585 
586  OMX_PARAM_U32TYPE u;
587  OMX_DATA_INIT(u);
588  u.nPortIndex = m_audiorender.Base();
589  OMX_ERRORTYPE e = m_audiorender.GetConfig(OMX_IndexConfigAudioRenderingLatency, &u);
590  if (e != OMX_ErrorNone)
591  {
592  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
593  "GetConfig AudioRenderingLatency error %1").arg(Error2String(e)));
594  return 0;
595  }
596  return u.nU32 * obpf / 80;
597 #else
598  return m_pending;
599 #endif
600 }
601 
602 // virtual
604 {
605  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " begin");
606 
607  if (!m_audiorender.IsValid())
608  {
609  LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + " No audio render");
610  return nullptr;
611  }
612 
614 
615  OMX_ERRORTYPE e = OMX_ErrorNone;
616  OMX_AUDIO_PARAM_PORTFORMATTYPE fmt;
617  OMX_DATA_INIT(fmt);
618  fmt.nPortIndex = m_audiorender.Base();
619  fmt.eEncoding = OMX_AUDIO_CodingPCM;
620  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
621  if (e != OMX_ErrorNone)
622  {
623  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
624  "SetParameter AudioPortFormat PCM error %1")
625  .arg(Error2String(e)));
626  return nullptr;
627  }
628 
629  OMX_AUDIO_PARAM_PCMMODETYPE pcm;
630  OMX_DATA_INIT(pcm);
631  pcm.nPortIndex = m_audiorender.Base();
632  e = m_audiorender.GetParameter(OMX_IndexParamAudioPcm, &pcm);
633  if (e != OMX_ErrorNone)
634  {
635  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
636  "GetParameter AudioPcm error %1")
637  .arg(Error2String(e)));
638  return nullptr;
639  }
640 
641  AudioOutputSettings *settings = new AudioOutputSettings();
642 
643  while (int rate = settings->GetNextRate())
644  {
645  pcm.nSamplingRate = rate;
646  if (OMX_ErrorNone == m_audiorender.SetParameter(OMX_IndexParamAudioPcm, &pcm))
647  settings->AddSupportedRate(rate);
648  }
649 
650  m_audiorender.GetParameter(OMX_IndexParamAudioPcm, &pcm);
651 
652  AudioFormat afmt = FORMAT_NONE;
653  while ((afmt = settings->GetNextFormat()))
654  {
655  if (::Format2Pcm(pcm, afmt) &&
656  OMX_ErrorNone == m_audiorender.SetParameter(OMX_IndexParamAudioPcm, &pcm))
657  {
658  settings->AddSupportedFormat(afmt);
659  }
660  }
661 
662  m_audiorender.GetParameter(OMX_IndexParamAudioPcm, &pcm);
663 
664  for (uint channels = 1; channels <= 8; channels++)
665  {
666 #ifdef USING_BELLAGIO // v0.9.3 aborts on 6 channels
667  if (channels == 6)
668  continue;
669 #endif
670  pcm.nChannels = channels;
671  if (OMX_ErrorNone == m_audiorender.SetParameter(OMX_IndexParamAudioPcm, &pcm))
672  settings->AddSupportedChannels(channels);
673  }
674 
675  settings->setFeature(FEATURE_LPCM);
676  settings->setPassthrough(-1);
677  if (m_main_device.contains(":hdmi"))
678  {
679 #ifdef OMX_AUDIO_CodingDDP_Supported
680  fmt.eEncoding = OMX_AUDIO_CodingDDP;
681  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
682  if (e == OMX_ErrorNone)
683  {
684  OMX_AUDIO_PARAM_DDPTYPE ddp;
685  OMX_DATA_INIT(ddp);
686  ddp.nPortIndex = m_audiorender.Base();
687  e = m_audiorender.GetParameter(OMX_IndexParamAudioDdp, &ddp);
688  if (e == OMX_ErrorNone)
689  {
690  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " (E)AC3 supported");
691  settings->setFeature(FEATURE_AC3);
692  settings->setFeature(FEATURE_EAC3);
693  settings->setPassthrough(1);
694  }
695  }
696 #endif
697 #ifdef OMX_AUDIO_CodingDTS_Supported
698  fmt.eEncoding = OMX_AUDIO_CodingDTS;
699  e = m_audiorender.SetParameter(OMX_IndexParamAudioPortFormat, &fmt);
700  if (e == OMX_ErrorNone)
701  {
702  OMX_AUDIO_PARAM_DTSTYPE dts;
703  OMX_DATA_INIT(dts);
704  dts.nPortIndex = m_audiorender.Base();
705  e = m_audiorender.GetParameter(OMX_IndexParamAudioDts, &dts);
706  if (e == OMX_ErrorNone)
707  {
708  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " DTS supported");
709  settings->setFeature(FEATURE_DTS);
710  settings->setFeature(FEATURE_DTSHD);
711  settings->setPassthrough(1);
712  }
713  }
714 #endif
715  }
716 
717  LOG(VB_AUDIO, LOG_INFO, LOC + __func__ + " end");
718  return settings;
719 }
720 
721 // virtual // Returns 0-100
722 int AudioOutputOMX::GetVolumeChannel(int channel) const
723 {
724  if (channel > 0)
725  return -1;
726 
727  OMX_AUDIO_CONFIG_VOLUMETYPE v;
728  OMX_DATA_INIT(v);
729  v.nPortIndex = m_audiorender.Base();
730  v.bLinear = OMX_TRUE;
731  OMX_ERRORTYPE e = m_audiorender.GetConfig(OMX_IndexConfigAudioVolume, &v);
732  if (e != OMX_ErrorNone)
733  {
734  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
735  "GetConfig AudioVolume error %1").arg(Error2String(e)));
736  return -1;
737  }
738 
739  return v.sVolume.nValue;
740 }
741 
742 // virtual // range 0-100 for vol
743 void AudioOutputOMX::SetVolumeChannel(int channel, int volume)
744 {
745  if (channel > 0)
746  return;
747 
748  OMX_AUDIO_CONFIG_VOLUMETYPE v;
749  OMX_DATA_INIT(v);
750  v.nPortIndex = m_audiorender.Base();
751  v.bLinear = OMX_TRUE;
752  v.sVolume.nValue = volume;
753  OMX_ERRORTYPE e = m_audiorender.SetConfig(OMX_IndexConfigAudioVolume, &v);
754  if (e != OMX_ErrorNone)
755  {
756  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
757  "SetConfig AudioVolume error %1").arg(Error2String(e)));
758  }
759 }
760 
762 {
763  QString mixerDevice = gCoreContext->GetSetting("MixerDevice", "OpenMAX:");
764  if (!mixerDevice.startsWith("OpenMAX:"))
765  return true;
766 
767  if (m_set_initial_vol)
768  {
769  QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM")
770  + "MixerVolume";
771  int initial_vol = gCoreContext->GetNumSetting(controlLabel, 80);
772  SetVolumeChannel(0, initial_vol);
773  }
774 
775  return true;
776 }
777 
778 // virtual
780  OMXComponent& /*cmpnt*/, OMX_BUFFERHEADERTYPE *hdr)
781 {
782  assert(hdr->nSize == sizeof(OMX_BUFFERHEADERTYPE));
783  assert(hdr->nVersion.nVersion == OMX_VERSION);
784  int nFilledLen = hdr->nFilledLen;
785  hdr->nFilledLen = 0;
786  hdr->nFlags = 0;
787  if (m_lock.tryLock(1000))
788  {
789  m_ibufs.append(hdr);
790  m_lock.unlock();
791  m_pending.fetchAndAddRelaxed(-nFilledLen);
792  m_ibufs_sema.release();
793  }
794  else
795  LOG(VB_GENERAL, LOG_CRIT, LOC + "EmptyBufferDone deadlock");
796  return OMX_ErrorNone;
797 }
798 
799 // Shutdown OMX_StateIdle -> OMX_StateLoaded callback
800 // virtual
802 {
803  FreeBuffersCB();
804 }
805 
806 // Free all OMX buffers
807 // OMX_CommandPortDisable callback
808 OMX_ERRORTYPE AudioOutputOMX::FreeBuffersCB()
809 {
811 
812  // Free all input buffers
813  while (m_ibufs_sema.tryAcquire())
814  {
815  m_lock.lock();
816  assert(!m_ibufs.isEmpty());
817  OMX_BUFFERHEADERTYPE *hdr = m_ibufs.takeFirst();
818  m_lock.unlock();
819 
820  OMX_ERRORTYPE e = OMX_ErrorNone;
821  e = OMX_FreeBuffer(m_audiorender.Handle(), m_audiorender.Base(), hdr);
822  if (e != OMX_ErrorNone)
823  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
824  "OMX_FreeBuffer 0x%1 error %2")
825  .arg(quintptr(hdr),0,16).arg(Error2String(e)));
826  }
827  return OMX_ErrorNone;
828 }
829 
830 // OMX_StateIdle callback
831 OMX_ERRORTYPE AudioOutputOMX::AllocBuffersCB()
832 {
834  assert(m_ibufs_sema.available() == 0);
835  assert(m_ibufs.isEmpty());
836 
837  // Allocate input buffers
838  const OMX_PARAM_PORTDEFINITIONTYPE &def = m_audiorender.PortDef();
839  OMX_U32 uBufs = def.nBufferCountActual;
840  LOG(VB_AUDIO, LOG_DEBUG, LOC + QString(
841  "Allocate %1 of %2 byte input buffer(s)")
842  .arg(uBufs).arg(def.nBufferSize));
843  while (uBufs--)
844  {
845  OMX_BUFFERHEADERTYPE *hdr = nullptr;
846  OMX_ERRORTYPE e = OMX_AllocateBuffer(m_audiorender.Handle(), &hdr,
847  def.nPortIndex, this, def.nBufferSize);
848  if (e != OMX_ErrorNone)
849  {
850  LOG(VB_AUDIO, LOG_ERR, LOC + QString(
851  "OMX_AllocateBuffer error %1").arg(Error2String(e)) );
852  return e;
853  }
854  if (hdr->nSize != sizeof(OMX_BUFFERHEADERTYPE))
855  {
856  LOG(VB_AUDIO, LOG_ERR, LOC + "OMX_AllocateBuffer header mismatch");
857  OMX_FreeBuffer(m_audiorender.Handle(), def.nPortIndex, hdr);
858  return OMX_ErrorVersionMismatch;
859  }
860  if (hdr->nVersion.nVersion != OMX_VERSION)
861  {
862  LOG(VB_AUDIO, LOG_ERR, LOC + "OMX_AllocateBuffer version mismatch");
863  OMX_FreeBuffer(m_audiorender.Handle(), def.nPortIndex, hdr);
864  return OMX_ErrorVersionMismatch;
865  }
866  hdr->nFilledLen = 0;
867  hdr->nOffset = 0;
868  m_lock.lock();
869  m_ibufs.append(hdr);
870  m_lock.unlock();
871  m_ibufs_sema.release();
872  }
873  return OMX_ErrorNone;
874 }
875 
876 // EOF
OMX_ERRORTYPE GetConfig(OMX_INDEXTYPE type, OMX_PTR p) const
Definition: omxcontext.h:73
static void SetupChannels(T &t)
ComponentCB FreeBuffersCB
void InitSettings(const AudioSettings &settings)
#define AUDIO_RENDER
QSemaphore m_ibufs_sema
AudioOutputOMX(const AudioSettings &settings)
#define REORD_A
QString toString(MarkTypes type)
OMX_U32 Base() const
Definition: omxcontext.h:48
void setPassthrough(int val)
bool IsValid() const
Definition: omxcontext.h:43
OMX_ERRORTYPE SetConfig(OMX_INDEXTYPE type, OMX_PTR p)
Definition: omxcontext.h:71
const OMX_PARAM_PORTDEFINITIONTYPE & PortDef(unsigned index=0) const
Definition: omxcontext.cpp:297
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define S64_TO_TICKS(n)
Definition: omxcontext.h:181
AudioOutputSettings * GetOutputSettings(bool passthrough) override
static const char * FormatToString(AudioFormat format)
void reorderChannels(int *aubuf, int size)
void ShowPortDef(unsigned index=0, LogLevel_t=LOG_INFO, uint64_t=VB_PLAYBACK) const
Definition: omxcontext.cpp:309
OMX_ERRORTYPE EmptyBufferDone(OMXComponent &, OMX_BUFFERHEADERTYPE *) override
OMX_STATETYPE GetState()
Definition: omxcontext.cpp:485
int GetVolumeChannel(int channel) const override
#define CASE2STR(f)
OMX_ERRORTYPE Init(OMX_INDEXTYPE)
Definition: omxcontext.cpp:218
OMX_ERRORTYPE SetState(OMX_STATETYPE state, int ms=-1, OMXComponentAbstractCB *cb=nullptr)
Definition: omxcontext.cpp:457
void SetVolumeChannel(int channel, int volume) override
#define REORD_B
QString GetSetting(const QString &key, const QString &defaultval="")
void setFeature(DigitalFeature arg)
set the provided digital feature possible values are:
QAtomicInt m_pending
bool usesSpdif() const
unsigned char t
Definition: ParseText.cpp:329
OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE type, OMX_PTR p) const
Definition: omxcontext.h:68
~AudioOutputOMX() override
AudioFormat m_output_format
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
void Shutdown()
Definition: omxcontext.cpp:185
static bool Format2Pcm(OMX_AUDIO_PARAM_PCMMODETYPE &pcm, AudioFormat afmt)
void OMX_DATA_INIT(T &s)
Definition: omxcontext.h:162
bool internal_vol
Definition: volumebase.h:41
#define REORD_NUMCHAN
void WriteAudio(unsigned char *aubuf, int size) override
OMX_ERRORTYPE PortDisable(unsigned index=0, int ms=-1, OMXComponentAbstractCB *cb=nullptr)
Definition: omxcontext.h:79
#define LOC
int GetNumSetting(const QString &key, int defaultval=0)
OMXComponent m_audiorender
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void ReleaseBuffers(OMXComponent &) override
#define assert(x)
void AddSupportedRate(int rate)
void KillAudio(void)
Kill the output thread and cleanup.
OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE type, OMX_PTR p)
Definition: omxcontext.h:66
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
const char * Error2String(OMX_ERRORTYPE eError)
void AddSupportedFormat(AudioFormat format)
QList< OMX_BUFFERHEADERTYPE * > m_ibufs
void AddSupportedChannels(int channels)
ComponentCB AllocBuffersCB
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:80
OMX_ERRORTYPE GetPortDef(unsigned index=0)
Definition: omxcontext.cpp:274
bool OpenDevice(void) override
void CloseDevice(void) override
unsigned Ports() const
Definition: omxcontext.h:49
void ShowFormats(unsigned index=0, LogLevel_t=LOG_INFO, uint64_t=VB_PLAYBACK) const
Definition: omxcontext.cpp:438
#define OMX_VERSION
Definition: omxcontext.h:158
OMX_HANDLETYPE Handle() const
Definition: omxcontext.h:44