MythTV master
audiooutput.cpp
Go to the documentation of this file.
1#include "libmythbase/mythconfig.h"
2
3#include <cstdio>
4#include <cstdlib>
5
6// Qt utils: to parse audio list
7#include <QtGlobal>
8#include <QFile>
9#include <QDateTime>
10#include <QDir>
11
12#include "libmythbase/compat.h"
15
16#include "audiooutput.h"
17#include "audiooutputnull.h"
18#ifdef _WIN32
19#include "audiooutputdx.h"
20#include "audiooutputwin.h"
21#endif
22#if CONFIG_AUDIO_OSS
23#include "audiooutputoss.h"
24#endif
25#if CONFIG_AUDIO_ALSA
26#include "audiooutputalsa.h"
27#endif
28#ifdef Q_OS_DARWIN
29#include "audiooutputca.h"
30#endif
31#if CONFIG_AUDIO_JACK
32#include "audiooutputjack.h"
33#endif
34#if CONFIG_AUDIO_PULSEOUTPUT
35#include "audiooutputpulse.h"
36#endif
37#if CONFIG_AUDIO_PULSE
38#include "audiopulsehandler.h"
39#endif
40#ifdef Q_OS_ANDROID
41#include "audiooutputopensles.h"
43#endif
44
45extern "C" {
46#include "libavcodec/avcodec.h" // to get codec id
47}
48#include "audioconvert.h"
49#include "mythaverror.h"
50
51#define LOC QString("AO: ")
52
54{
55#if CONFIG_AUDIO_PULSE
57#endif
58}
59
61 const QString &main_device, const QString &passthru_device,
62 AudioFormat format, int channels, AVCodecID codec, int samplerate,
63 AudioOutputSource source, bool set_initial_vol, bool passthru,
64 int upmixer_startup, AudioOutputSettings *custom)
65{
66 AudioSettings settings(
67 main_device, passthru_device, format, channels, codec, samplerate,
68 source, set_initial_vol, passthru, upmixer_startup, custom);
69
70 return OpenAudio(settings);
71}
72
74 const QString &main_device, const QString &passthru_device,
75 bool willsuspendpa)
76{
77 AudioSettings settings(main_device, passthru_device);
78
79 return OpenAudio(settings, willsuspendpa);
80}
81
83 [[maybe_unused]] bool willsuspendpa)
84{
85 QString &main_device = settings.m_mainDevice;
86 AudioOutput *ret = nullptr;
87
88 // Don't suspend Pulse if unnecessary. This can save 100mS
89 if (settings.m_format == FORMAT_NONE || settings.m_channels <= 0)
90 willsuspendpa = false;
91
92#if CONFIG_AUDIO_PULSE
93 bool pulsestatus = false;
94#else
95 {
96 static bool warned = false;
97 if (!warned && IsPulseAudioRunning())
98 {
99 warned = true;
100 LOG(VB_GENERAL, LOG_WARNING,
101 "WARNING: ***Pulse Audio is running***");
102 }
103 }
104#endif // CONFIG_AUDIO_PULSE
105
106 settings.FixPassThrough();
107
108 if (main_device.startsWith("PulseAudio:"))
109 {
110#if CONFIG_AUDIO_PULSEOUTPUT
111 return new AudioOutputPulseAudio(settings);
112#else
113 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to PulseAudio "
114 "but PulseAudio support is not compiled in!");
115 return nullptr;
116#endif // CONFIG_AUDIO_PULSEOUTPUT
117 }
118 if (main_device.startsWith("NULL"))
119 {
120 return new AudioOutputNULL(settings);
121 }
122
123#if CONFIG_AUDIO_PULSE
124 if (willsuspendpa)
125 {
126 bool ispulse = false;
127#if CONFIG_AUDIO_ALSA
128 // Check if using ALSA, that the device doesn't contain the word
129 // "pulse" in its hint
130 if (main_device.startsWith("ALSA:"))
131 {
132 QString device_name = main_device;
133
134 device_name.remove(0, 5);
135 QMap<QString, QString> *alsadevs =
137 if (!alsadevs->empty() && alsadevs->contains(device_name))
138 {
139 if (alsadevs->value(device_name).contains("pulse",
140 Qt::CaseInsensitive))
141 {
142 ispulse = true;
143 }
144 }
145 delete alsadevs;
146 }
147#endif // CONFIF_AUDIO_ALSA
148 if (main_device.contains("pulse", Qt::CaseInsensitive))
149 {
150 ispulse = true;
151 }
152 if (!ispulse)
153 {
155 }
156 }
157#endif // CONFIG_AUDIO_PULSE
158
159 if (main_device.startsWith("ALSA:"))
160 {
161#if CONFIG_AUDIO_ALSA
162 settings.TrimDeviceType();
163 ret = new AudioOutputALSA(settings);
164#else
165 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to an ALSA device "
166 "but ALSA support is not compiled in!");
167#endif
168 }
169 else if (main_device.startsWith("JACK:"))
170 {
171#if CONFIG_AUDIO_JACK
172 settings.TrimDeviceType();
173 ret = new AudioOutputJACK(settings);
174#else
175 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a JACK device "
176 "but JACK support is not compiled in!");
177#endif
178 }
179 else if (main_device.startsWith("DirectX:"))
180 {
181#ifdef _WIN32
182 ret = new AudioOutputDX(settings);
183#else
184 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to DirectX device "
185 "but DirectX support is not compiled in!");
186#endif
187 }
188 else if (main_device.startsWith("Windows:"))
189 {
190#ifdef _WIN32
191 ret = new AudioOutputWin(settings);
192#else
193 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a Windows "
194 "device but Windows support is not compiled "
195 "in!");
196#endif
197 }
198 else if (main_device.startsWith("OpenSLES:"))
199 {
200#ifdef Q_OS_ANDROID
201 ret = new AudioOutputOpenSLES(settings);
202#else
203 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to a OpenSLES "
204 "device but Android support is not compiled "
205 "in!");
206#endif
207 }
208 else if (main_device.startsWith("AudioTrack:"))
209 {
210#ifdef Q_OS_ANDROID
211 ret = new AudioOutputAudioTrack(settings);
212#else
213 LOG(VB_GENERAL, LOG_ERR, "Audio output device is set to AudioTrack "
214 "device but Android support is not compiled "
215 "in!");
216#endif
217 }
218#if CONFIG_AUDIO_OSS
219 else
220 {
221 ret = new AudioOutputOSS(settings);
222 }
223#elif defined(Q_OS_DARWIN)
224 else
225 {
226 ret = new AudioOutputCA(settings);
227 }
228#endif
229
230 if (!ret)
231 {
232 LOG(VB_GENERAL, LOG_CRIT, "No useable audio output driver found.");
233 LOG(VB_GENERAL, LOG_ERR, "Don't disable OSS support unless you're "
234 "not running on Linux.");
235#if CONFIG_AUDIO_PULSE
236 if (pulsestatus)
238#endif
239 return nullptr;
240 }
241#if CONFIG_AUDIO_PULSE
242 ret->m_pulseWasSuspended = pulsestatus;
243#endif
244 return ret;
245}
246
248{
249#if CONFIG_AUDIO_PULSE
252#endif
253 av_frame_free(&m_frame);
254}
255
256void AudioOutput::SetStretchFactor(float /*factor*/)
257{
258}
259
261{
262 return new AudioOutputSettings;
263}
264
266{
267 return new AudioOutputSettings;
268}
269
270bool AudioOutput::CanPassthrough(int /*samplerate*/,
271 int /*channels*/,
272 AVCodecID /*codec*/,
273 int /*profile*/) const
274{
275 return false;
276}
277
278// TODO: get rid of this if possible... need to see what uses GetError() and
279// GetWarning() and why. These would give more useful logs as macros
280void AudioOutput::Error(const QString &msg)
281{
282 m_lastError = msg;
283 LOG(VB_GENERAL, LOG_ERR, "AudioOutput Error: " + m_lastError);
284}
285
286void AudioOutput::SilentError(const QString &msg)
287{
288 m_lastError = msg;
289}
290
291void AudioOutput::Warn(const QString &msg)
292{
293 m_lastWarn = msg;
294 LOG(VB_GENERAL, LOG_WARNING, "AudioOutput Warning: " + m_lastWarn);
295}
296
298{
299 m_lastError.clear();
300}
301
303{
304 m_lastWarn.clear();
305}
306
308 QString &name, const QString &desc, bool willsuspendpa)
309{
310 AudioOutputSettings aosettings(true);
311
312 AudioOutput *ao = OpenAudio(name, QString(), willsuspendpa);
313 if (ao)
314 {
315 aosettings = *(ao->GetOutputSettingsCleaned());
316 delete ao;
317 }
318 if (aosettings.IsInvalid())
319 {
320 if (!willsuspendpa)
321 return nullptr;
322 QString msg = tr("Invalid or unuseable audio device");
323 return new AudioOutput::AudioDeviceConfig(name, msg);
324 }
325
326 QString capabilities = desc;
327 int max_channels = aosettings.BestSupportedChannelsELD();
328 if (aosettings.hasELD())
329 {
330 if (aosettings.getELD().isValid())
331 {
332 capabilities += tr(" (%1 connected to %2)")
333 .arg(aosettings.getELD().product_name().simplified(),
334 aosettings.getELD().connection_name());
335 }
336 else
337 {
338 capabilities += tr(" (No connection detected)");
339 }
340 }
341
342 QString speakers;
343 switch (max_channels)
344 {
345 case 6:
346 speakers = "5.1";
347 break;
348 case 8:
349 speakers = "7.1";
350 break;
351 default:
352 speakers = "2.0";
353 break;
354 }
355
356 capabilities += tr("\nDevice supports up to %1")
357 .arg(speakers);
358 if (aosettings.canPassthrough() >= 0)
359 {
360 if (aosettings.hasELD() && aosettings.getELD().isValid())
361 {
362 // We have an ELD, show actual reported capabilities
363 capabilities += " (" + aosettings.getELD().codecs_desc() + ")";
364 }
365 else
366 {
367 // build capabilities string, in a similar fashion as reported
368 // by ELD
369 int mask = 0;
370 mask |=
371 (static_cast<int>(aosettings.canLPCM()) << 0) |
372 (static_cast<int>(aosettings.canAC3()) << 1) |
373 (static_cast<int>(aosettings.canDTS()) << 2);
374 static const std::array<const std::string,3> s_typeNames { "LPCM", "AC3", "DTS" };
375
376 if (mask != 0)
377 {
378 capabilities += QObject::tr(" (guessing: ");
379 bool found_one = false;
380 for (unsigned int i = 0; i < 3; i++)
381 {
382 if ((mask & (1 << i)) != 0)
383 {
384 if (found_one)
385 capabilities += ", ";
386 capabilities += QString::fromStdString(s_typeNames[i]);
387 found_one = true;
388 }
389 }
390 capabilities += QString(")");
391 }
392 }
393 }
394 LOG(VB_AUDIO, LOG_INFO, QString("Found %1 (%2)") .arg(name, capabilities));
395 auto *adc = new AudioOutput::AudioDeviceConfig(name, capabilities);
396 adc->m_settings = aosettings;
397 return adc;
398}
399
400#if CONFIG_AUDIO_OSS
401static void fillSelectionsFromDir(const QDir &dir,
403{
404 QFileInfoList entries = dir.entryInfoList();
405 for (const auto& fi : std::as_const(entries))
406 {
407 QString name = fi.absoluteFilePath();
408 QString desc = AudioOutput::tr("OSS device");
411 if (!adc)
412 continue;
413 list->append(*adc);
414 delete adc;
415 }
416}
417#endif
418
420{
421 auto *list = new ADCVect;
422
423#if CONFIG_AUDIO_PULSE
425#endif
426
427#if CONFIG_AUDIO_ALSA
428 QMap<QString, QString> *alsadevs = AudioOutputALSA::GetDevices("pcm");
429
430 if (!alsadevs->empty())
431 {
432 for (auto i = alsadevs->cbegin(); i != alsadevs->cend(); ++i)
433 {
434 const QString& key = i.key();
435 const QString& desc = i.value();
436 QString devname = QString("ALSA:%1").arg(key);
437
438 auto *adc = GetAudioDeviceConfig(devname, desc);
439 if (!adc)
440 continue;
441 list->append(*adc);
442 delete adc;
443 }
444 }
445 delete alsadevs;
446#endif
447#if CONFIG_AUDIO_OSS
448 {
449 QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
450 fillSelectionsFromDir(dev, list);
451 dev.setNameFilters(QStringList("adsp*"));
452 fillSelectionsFromDir(dev, list);
453
454 dev.setPath("/dev/sound");
455 if (dev.exists())
456 {
457 dev.setNameFilters(QStringList("dsp*"));
458 fillSelectionsFromDir(dev, list);
459 dev.setNameFilters(QStringList("adsp*"));
460 fillSelectionsFromDir(dev, list);
461 }
462 }
463#endif
464#if CONFIG_AUDIO_JACK
465 {
466 QString name = "JACK:";
467 QString desc = tr("Use JACK default sound server.");
468 auto *adc = GetAudioDeviceConfig(name, desc);
469 if (adc)
470 {
471 list->append(*adc);
472 delete adc;
473 }
474 }
475#endif
476#ifdef Q_OS_DARWIN
477
478 {
479 QMap<QString, QString> *devs = AudioOutputCA::GetDevices(nullptr);
480 if (!devs->empty())
481 {
482 for (QMap<QString, QString>::const_iterator i = devs->begin();
483 i != devs->end(); ++i)
484 {
485 QString key = i.key();
486 QString desc = i.value();
487 QString devname = QString("CoreAudio:%1").arg(key);
488
489 auto adc = GetAudioDeviceConfig(devname, desc);
490 if (!adc)
491 continue;
492 list->append(*adc);
493 delete adc;
494 }
495 }
496 delete devs;
497 QString name = "CoreAudio:Default Output Device";
498 QString desc = tr("CoreAudio default output");
499 auto adc = GetAudioDeviceConfig(name, desc);
500 if (adc)
501 {
502 list->append(*adc);
503 delete adc;
504 }
505 }
506#endif
507#ifdef _WIN32
508 {
509 QString name = "Windows:";
510 QString desc = "Windows default output";
511 auto adc = GetAudioDeviceConfig(name, desc);
512 if (adc)
513 {
514 list->append(*adc);
515 delete adc;
516 }
517
518 QMap<int, QString> *dxdevs = AudioOutputDX::GetDXDevices();
519
520 if (!dxdevs->empty())
521 {
522 for (QMap<int, QString>::const_iterator i = dxdevs->begin();
523 i != dxdevs->end(); ++i)
524 {
525 QString devdesc = i.value();
526 QString devname = QString("DirectX:%1").arg(devdesc);
527
528 adc = GetAudioDeviceConfig(devname, devdesc);
529 if (!adc)
530 continue;
531 list->append(*adc);
532 delete adc;
533 }
534 }
535 delete dxdevs;
536 }
537#endif
538
539#if CONFIG_AUDIO_PULSE
540 if (pasuspended)
542#endif
543
544#if CONFIG_AUDIO_PULSEOUTPUT
545 {
546 QString name = "PulseAudio:default";
547 QString desc = tr("PulseAudio default sound server.");
548 auto *adc = GetAudioDeviceConfig(name, desc);
549 if (adc)
550 {
551 list->append(*adc);
552 delete adc;
553 }
554 }
555#endif
556
557#ifdef Q_OS_ANDROID
558 {
559 QString name = "OpenSLES:";
560 QString desc = tr("OpenSLES default output. Stereo support only.");
561 auto adc = GetAudioDeviceConfig(name, desc);
562 if (adc)
563 {
564 list->append(*adc);
565 delete adc;
566 }
567 }
568 {
569 QString name = "AudioTrack:";
570 QString desc = tr("Android AudioTrack output. Supports surround sound.");
571 auto adc = GetAudioDeviceConfig(name, desc);
572 if (adc)
573 {
574 list->append(*adc);
575 delete adc;
576 }
577 }
578#endif
579
580 QString name = "NULL";
581 QString desc = "NULL device";
582 auto *adc = GetAudioDeviceConfig(name, desc);
583 if (adc)
584 {
585 list->append(*adc);
586 delete adc;
587 }
588 return list;
589}
590
598int AudioOutput::DecodeAudio(AVCodecContext *ctx,
599 uint8_t *buffer, int &data_size,
600 const AVPacket *pkt)
601{
602 bool got_frame = false;
603
604 data_size = 0;
605 if (!m_frame)
606 {
607 m_frame = av_frame_alloc();
608 if (m_frame == nullptr)
609 {
610 return AVERROR(ENOMEM);
611 }
612 }
613 else
614 {
615 av_frame_unref(m_frame);
616 }
617
618// SUGGESTION
619// Now that avcodec_decode_audio4 is deprecated and replaced
620// by 2 calls (receive frame and send packet), this could be optimized
621// into separate routines or separate threads.
622// Also now that it always consumes a whole buffer some code
623// in the caller may be able to be optimized.
624 int ret = avcodec_receive_frame(ctx,m_frame);
625 if (ret == 0)
626 got_frame = true;
627 if (ret == AVERROR(EAGAIN))
628 ret = 0;
629 if (ret == 0)
630 ret = avcodec_send_packet(ctx, pkt);
631 if (ret == AVERROR(EAGAIN))
632 ret = 0;
633 else if (ret < 0)
634 {
635 std::string error;
636 LOG(VB_AUDIO, LOG_ERR, LOC +
637 QString("audio decode error: %1 (%2)")
639 .arg(got_frame));
640 return ret;
641 }
642 else
643 {
644 ret = pkt->size;
645 }
646
647 if (!got_frame)
648 {
649 LOG(VB_AUDIO, LOG_DEBUG, LOC +
650 QString("audio decode, no frame decoded (%1)").arg(ret));
651 return ret;
652 }
653
654 auto format = (AVSampleFormat)m_frame->format;
655 AudioFormat fmt =
656 AudioOutputSettings::AVSampleFormatToFormat(format, ctx->bits_per_raw_sample);
657
658 data_size = m_frame->nb_samples * m_frame->ch_layout.nb_channels * av_get_bytes_per_sample(format);
659
660 // May need to convert audio to S16
661 AudioConvert converter(fmt, CanProcess(fmt) ? fmt : FORMAT_S16);
662 uint8_t* src = nullptr;
663
664 if (av_sample_fmt_is_planar(format))
665 {
666 src = buffer;
667 converter.InterleaveSamples(m_frame->ch_layout.nb_channels,
668 src,
669 (const uint8_t **)m_frame->extended_data,
670 data_size);
671 }
672 else
673 {
674 // data is already compacted...
675 src = m_frame->extended_data[0];
676 }
677
678 uint8_t* transit = buffer;
679
680 if (!CanProcess(fmt) &&
681 av_get_bytes_per_sample(ctx->sample_fmt) < AudioOutputSettings::SampleSize(converter.Out()))
682 {
683 // this conversion can't be done in place
684 transit = (uint8_t*)av_malloc(data_size * av_get_bytes_per_sample(ctx->sample_fmt)
685 / AudioOutputSettings::SampleSize(converter.Out()));
686 if (!transit)
687 {
688 LOG(VB_AUDIO, LOG_ERR, LOC +
689 QString("audio decode, out of memory"));
690 data_size = 0;
691 return ret;
692 }
693 }
694 if (!CanProcess(fmt) || src != transit)
695 {
696 data_size = converter.Process(transit, src, data_size, true);
697 }
698 if (transit != buffer)
699 {
700 av_free(transit);
701 }
702 return ret;
703}
#define LOC
Definition: audiooutput.cpp:51
@ FORMAT_NONE
@ FORMAT_S16
AudioOutputSource
Definition: audiosettings.h:21
AudioFormat Out(void)
Definition: audioconvert.h:49
void InterleaveSamples(int channels, uint8_t *output, const uint8_t *const *input, int data_size)
int Process(void *out, const void *in, int bytes, bool noclip=false)
Process Parameters: out : destination buffer where converted samples will be copied in : source buffe...
static QMap< QString, QString > * GetDevices(const char *type)
Implements Core Audio (Mac OS X Hardware Abstraction Layer) output.
Definition: audiooutputca.h:14
static QMap< QString, QString > * GetDevices(const char *type=nullptr)
static QMap< int, QString > * GetDXDevices(void)
eld & getELD(void)
retrieve ELD data
static int SampleSize(AudioFormat format)
bool canLPCM() const
return true if device supports multichannels PCM (deprecated, see canFeature())
bool canAC3() const
return true if device can or may support AC3 (deprecated, see canFeature())
int BestSupportedChannelsELD()
Reports best supported channel number, restricted to ELD range.
bool hasELD() const
get the ELD flag
static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits=0)
Return AVSampleFormat closest equivalent to AudioFormat.
bool canDTS() const
return true if device can or may support DTS (deprecated, see canFeature())
bool IsInvalid() const
return true if class instance is marked invalid.
void ClearWarning(void)
~AudioOutput() override
static AudioOutput * OpenAudio(const QString &main_device, const QString &passthru_device, AudioFormat format, int channels, AVCodecID codec, int samplerate, AudioOutputSource source, bool set_initial_vol, bool passthru, int upmixer_startup=0, AudioOutputSettings *custom=nullptr)
Definition: audiooutput.cpp:60
static ADCVect * GetOutputList(void)
bool m_pulseWasSuspended
Definition: audiooutput.h:206
virtual AudioOutputSettings * GetOutputSettingsUsers(bool digital=true)
QVector< AudioDeviceConfig > ADCVect
Definition: audiooutput.h:47
void SilentError(const QString &msg)
void Error(const QString &msg)
virtual int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
Utility routine.
QString m_lastError
Definition: audiooutput.h:204
virtual bool CanPassthrough(int samplerate, int channels, AVCodecID codec, int profile) const
static AudioDeviceConfig * GetAudioDeviceConfig(QString &name, const QString &desc, bool willsuspendpa=false)
virtual AudioOutputSettings * GetOutputSettingsCleaned(bool digital=true)
void Warn(const QString &msg)
virtual uint32_t CanProcess(void)
Definition: audiooutput.h:173
void ClearError(void)
virtual void SetStretchFactor(float factor)
static void Cleanup(void)
Definition: audiooutput.cpp:53
QString m_lastWarn
Definition: audiooutput.h:205
AVFrame * m_frame
Definition: audiooutput.h:207
QString m_mainDevice
Definition: audiosettings.h:70
void TrimDeviceType(void)
AudioFormat m_format
Definition: audiosettings.h:72
void FixPassThrough(void)
void error(const QString &e)
Definition: output.cpp:29
static bool Suspend(enum PulseAction action)
QString codecs_desc() const
Definition: eldutils.cpp:471
bool isValid() const
Definition: eldutils.cpp:407
QString connection_name() const
Definition: eldutils.cpp:441
QString product_name() const
Definition: eldutils.cpp:436
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
Definition: mythaverror.cpp:42
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
uint fillSelectionsFromDir(const QDir &dir, uint minor_min, uint minor_max, const QString &card, const QRegularExpression &driver, bool allow_duplicates, V2CaptureDeviceList *pList, const QString &cardType)