1#include "libmythbase/mythconfig.h"
9#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
10#include <QtSystemDetection>
16#include <QMutexLocker>
40#if CONFIG_AUDIO_PULSEOUTPUT
52#include "libavcodec/avcodec.h"
58#define LOC QString("AO: ")
68 const QString &main_device,
const QString &passthru_device,
69 AudioFormat format,
int channels, AVCodecID codec,
int samplerate,
74 main_device, passthru_device, format,
channels, codec, samplerate,
75 source, set_initial_vol, passthru, upmixer_startup, custom);
81 const QString &main_device,
const QString &passthru_device,
86 return OpenAudio(settings, willsuspendpa);
90 [[maybe_unused]]
bool willsuspendpa)
97 willsuspendpa =
false;
100 bool pulsestatus =
false;
103 static bool warned =
false;
107 LOG(VB_GENERAL, LOG_WARNING,
108 "WARNING: ***Pulse Audio is running***");
115 if (main_device.startsWith(
"PulseAudio:"))
117#if CONFIG_AUDIO_PULSEOUTPUT
120 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to PulseAudio "
121 "but PulseAudio support is not compiled in!");
125 if (main_device.startsWith(
"NULL"))
130#if CONFIG_AUDIO_PULSE
133 bool ispulse =
false;
137 if (main_device.startsWith(
"ALSA:"))
139 QString device_name = main_device;
141 device_name.remove(0, 5);
142 QMap<QString, QString> *alsadevs =
144 if (!alsadevs->empty() && alsadevs->contains(device_name))
146 if (alsadevs->value(device_name).contains(
"pulse",
147 Qt::CaseInsensitive))
155 if (main_device.contains(
"pulse", Qt::CaseInsensitive))
166 if (main_device.startsWith(
"ALSA:"))
172 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to an ALSA device "
173 "but ALSA support is not compiled in!");
176 else if (main_device.startsWith(
"JACK:"))
182 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a JACK device "
183 "but JACK support is not compiled in!");
186 else if (main_device.startsWith(
"DirectX:"))
191 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to DirectX device "
192 "but DirectX support is not compiled in!");
195 else if (main_device.startsWith(
"Windows:"))
200 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a Windows "
201 "device but Windows support is not compiled "
205 else if (main_device.startsWith(
"OpenSLES:"))
210 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a OpenSLES "
211 "device but Android support is not compiled "
215 else if (main_device.startsWith(
"AudioTrack:"))
220 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to AudioTrack "
221 "device but Android support is not compiled "
230#elif defined(Q_OS_DARWIN)
239 LOG(VB_GENERAL, LOG_CRIT,
"No useable audio output driver found.");
240 LOG(VB_GENERAL, LOG_ERR,
"Don't disable OSS support unless you're "
241 "not running on Linux.");
242#if CONFIG_AUDIO_PULSE
248#if CONFIG_AUDIO_PULSE
256#if CONFIG_AUDIO_PULSE
286 QString &name,
const QString &desc,
bool willsuspendpa)
300 QString msg = tr(
"Invalid or unuseable audio device");
304 QString capabilities = desc;
310 capabilities += tr(
" (%1 connected to %2)")
316 capabilities += tr(
" (No connection detected)");
321 switch (max_channels)
334 capabilities += tr(
"\nDevice supports up to %1")
349 (
static_cast<int>(aosettings.
canLPCM()) << 0) |
350 (
static_cast<int>(aosettings.
canAC3()) << 1) |
351 (
static_cast<int>(aosettings.
canDTS()) << 2);
352 static const std::array<const std::string,3> s_typeNames {
"LPCM",
"AC3",
"DTS" };
356 capabilities += QObject::tr(
" (guessing: ");
357 bool found_one =
false;
358 for (
unsigned int i = 0; i < 3; i++)
360 if ((mask & (1 << i)) != 0)
363 capabilities +=
", ";
364 capabilities += QString::fromStdString(s_typeNames[i]);
368 capabilities += QString(
")");
372 LOG(VB_AUDIO, LOG_INFO, QString(
"Found %1 (%2)") .arg(name, capabilities));
374 adc->m_settings = aosettings;
382 QFileInfoList entries = dir.entryInfoList();
383 for (
const auto& fi : std::as_const(entries))
385 QString name = fi.absoluteFilePath();
386 QString desc = AudioOutput::tr(
"OSS device");
401#if CONFIG_AUDIO_PULSE
408 if (!alsadevs->empty())
410 for (
auto i = alsadevs->cbegin(); i != alsadevs->cend(); ++i)
412 const QString& key = i.key();
413 const QString& desc = i.value();
414 QString devname = QString(
"ALSA:%1").arg(key);
427 QDir dev(
"/dev",
"dsp*", QDir::Name, QDir::System);
429 dev.setNameFilters(QStringList(
"adsp*"));
432 dev.setPath(
"/dev/sound");
435 dev.setNameFilters(QStringList(
"dsp*"));
437 dev.setNameFilters(QStringList(
"adsp*"));
444 QString name =
"JACK:";
445 QString desc = tr(
"Use JACK default sound server.");
460 for (QMap<QString, QString>::const_iterator i = devs->begin();
461 i != devs->end(); ++i)
463 const QString& key = i.key();
464 const QString& desc = i.value();
465 QString devname = QString(
"CoreAudio:%1").arg(key);
475 QString name =
"CoreAudio:Default Output Device";
476 QString desc = tr(
"CoreAudio default output");
487 QString name =
"Windows:";
488 QString desc =
"Windows default output";
498 if (!dxdevs->empty())
500 for (QMap<int, QString>::const_iterator i = dxdevs->begin();
501 i != dxdevs->end(); ++i)
503 QString devdesc = i.value();
504 QString devname = QString(
"DirectX:%1").arg(devdesc);
517#if CONFIG_AUDIO_PULSE
522#if CONFIG_AUDIO_PULSEOUTPUT
524 QString name =
"PulseAudio:default";
525 QString desc = tr(
"PulseAudio default sound server.");
537 QString name =
"OpenSLES:";
538 QString desc = tr(
"OpenSLES default output. Stereo support only.");
547 QString name =
"AudioTrack:";
548 QString desc = tr(
"Android AudioTrack output. Supports surround sound.");
558 QString name =
"NULL";
559 QString desc =
"NULL device";
570 uint8_t *buffer,
int &data_size,
573 bool got_frame =
false;
581 return AVERROR(ENOMEM);
595 int ret = avcodec_receive_frame(ctx,
m_frame);
598 if (ret == AVERROR(EAGAIN))
601 ret = avcodec_send_packet(ctx, pkt);
602 if (ret == AVERROR(EAGAIN))
609 LOG(VB_AUDIO, LOG_ERR,
LOC +
610 QString(
"audio decode error: %1 (%2)")
622 LOG(VB_AUDIO, LOG_DEBUG,
LOC +
623 QString(
"audio decode, no frame decoded (%1)").arg(ret));
627 auto format = (AVSampleFormat)
m_frame->format;
631 data_size =
m_frame->nb_samples *
m_frame->ch_layout.nb_channels * av_get_bytes_per_sample(format);
635 uint8_t* src =
nullptr;
637 if (av_sample_fmt_is_planar(format))
642 (
const uint8_t **)
m_frame->extended_data,
648 src =
m_frame->extended_data[0];
651 uint8_t* transit = buffer;
657 transit = (uint8_t*)av_malloc(data_size * av_get_bytes_per_sample(ctx->sample_fmt)
661 LOG(VB_AUDIO, LOG_ERR,
LOC +
662 QString(
"audio decode, out of memory"));
669 data_size = converter.
Process(transit, src, data_size,
true);
671 if (transit != buffer)
686#ifdef __cpp_lib_ranges_contains
687 if (!std::ranges::contains(
m_visuals, v))
703 std::chrono::milliseconds timecode,
711 QMutexLocker locker(visual->mutex());
712 visual->add(buffer, b_len, timecode, chan, prec);
720 QMutexLocker locker(visual->mutex());
731 auto *samp16 = (int16_t*) frames;
732 auto *samp32 = (int32_t*) frames;
736 for(
int chn = 0 ; chn < channels; chn++)
742 static_cast<float>(0x03fffffff);
745 *samp16++ = qToLittleEndian<qint16>(ires >> 16);
747 *samp32++ = qToLittleEndian<qint32>(ires);
static char * generatePinkFrames(char *frames, int channels, int channel, int count, int bits)
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.
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())
int canPassthrough() const
bool IsInvalid() const
return true if class instance is marked invalid.
void addVisual(Visualization *v)
void removeVisual(Visualization *v)
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)
static ADCVect * GetOutputList(void)
virtual AudioOutputSettings * GetOutputSettingsUsers(bool digital=true)
QVector< AudioDeviceConfig > ADCVect
void dispatchVisual(uchar *b, unsigned long b_len, std::chrono::milliseconds timecode, int chan, int prec)
int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
Utility routine.
virtual bool CanPassthrough(int samplerate, int channels, AVCodecID codec, int profile) const
static AudioDeviceConfig * GetAudioDeviceConfig(QString &name, const QString &desc, bool willsuspendpa=false)
void dispatchError(const QString &e)
virtual bool AddFrames(void *buffer, int frames, std::chrono::milliseconds timecode)=0
Add frames to the audiobuffer for playback.
virtual AudioOutputSettings * GetOutputSettingsCleaned(bool digital=true)
virtual uint32_t CanProcess(void)
virtual void SetStretchFactor(float factor)
static void Cleanup(void)
bool playPinkNoise(char *frames, int channels, int channel, int count, int bits)
Generates and plays pink noise from a speaker for testing.
std::vector< Visualization * > m_visuals
const int & channels() const
void TrimDeviceType(void)
void FixPassThrough(void)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
static bool Suspend(enum PulseAction action)
QString codecs_desc() const
QString connection_name() const
QString product_name() const
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
void initialize_pink_noise(pink_noise_t *pink, int num_rows)
float generate_pink_noise_sample(pink_noise_t *pink)
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)