1#include "libmythbase/mythconfig.h"
13#include <QMutexLocker>
37#if CONFIG_AUDIO_PULSEOUTPUT
49#include "libavcodec/avcodec.h"
55#define LOC QString("AO: ")
65 const QString &main_device,
const QString &passthru_device,
66 AudioFormat format,
int channels, AVCodecID codec,
int samplerate,
71 main_device, passthru_device, format,
channels, codec, samplerate,
72 source, set_initial_vol, passthru, upmixer_startup, custom);
78 const QString &main_device,
const QString &passthru_device,
83 return OpenAudio(settings, willsuspendpa);
87 [[maybe_unused]]
bool willsuspendpa)
94 willsuspendpa =
false;
97 bool pulsestatus =
false;
100 static bool warned =
false;
104 LOG(VB_GENERAL, LOG_WARNING,
105 "WARNING: ***Pulse Audio is running***");
112 if (main_device.startsWith(
"PulseAudio:"))
114#if CONFIG_AUDIO_PULSEOUTPUT
117 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to PulseAudio "
118 "but PulseAudio support is not compiled in!");
122 if (main_device.startsWith(
"NULL"))
127#if CONFIG_AUDIO_PULSE
130 bool ispulse =
false;
134 if (main_device.startsWith(
"ALSA:"))
136 QString device_name = main_device;
138 device_name.remove(0, 5);
139 QMap<QString, QString> *alsadevs =
141 if (!alsadevs->empty() && alsadevs->contains(device_name))
143 if (alsadevs->value(device_name).contains(
"pulse",
144 Qt::CaseInsensitive))
152 if (main_device.contains(
"pulse", Qt::CaseInsensitive))
163 if (main_device.startsWith(
"ALSA:"))
169 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to an ALSA device "
170 "but ALSA support is not compiled in!");
173 else if (main_device.startsWith(
"JACK:"))
179 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a JACK device "
180 "but JACK support is not compiled in!");
183 else if (main_device.startsWith(
"DirectX:"))
188 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to DirectX device "
189 "but DirectX support is not compiled in!");
192 else if (main_device.startsWith(
"Windows:"))
197 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a Windows "
198 "device but Windows support is not compiled "
202 else if (main_device.startsWith(
"OpenSLES:"))
207 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to a OpenSLES "
208 "device but Android support is not compiled "
212 else if (main_device.startsWith(
"AudioTrack:"))
217 LOG(VB_GENERAL, LOG_ERR,
"Audio output device is set to AudioTrack "
218 "device but Android support is not compiled "
227#elif defined(Q_OS_DARWIN)
236 LOG(VB_GENERAL, LOG_CRIT,
"No useable audio output driver found.");
237 LOG(VB_GENERAL, LOG_ERR,
"Don't disable OSS support unless you're "
238 "not running on Linux.");
239#if CONFIG_AUDIO_PULSE
245#if CONFIG_AUDIO_PULSE
253#if CONFIG_AUDIO_PULSE
283 QString &name,
const QString &desc,
bool willsuspendpa)
297 QString msg = tr(
"Invalid or unuseable audio device");
301 QString capabilities = desc;
307 capabilities += tr(
" (%1 connected to %2)")
313 capabilities += tr(
" (No connection detected)");
318 switch (max_channels)
331 capabilities += tr(
"\nDevice supports up to %1")
346 (
static_cast<int>(aosettings.
canLPCM()) << 0) |
347 (
static_cast<int>(aosettings.
canAC3()) << 1) |
348 (
static_cast<int>(aosettings.
canDTS()) << 2);
349 static const std::array<const std::string,3> s_typeNames {
"LPCM",
"AC3",
"DTS" };
353 capabilities += QObject::tr(
" (guessing: ");
354 bool found_one =
false;
355 for (
unsigned int i = 0; i < 3; i++)
357 if ((mask & (1 << i)) != 0)
360 capabilities +=
", ";
361 capabilities += QString::fromStdString(s_typeNames[i]);
365 capabilities += QString(
")");
369 LOG(VB_AUDIO, LOG_INFO, QString(
"Found %1 (%2)") .arg(name, capabilities));
371 adc->m_settings = aosettings;
379 QFileInfoList entries = dir.entryInfoList();
380 for (
const auto& fi : std::as_const(entries))
382 QString name = fi.absoluteFilePath();
383 QString desc = AudioOutput::tr(
"OSS device");
398#if CONFIG_AUDIO_PULSE
405 if (!alsadevs->empty())
407 for (
auto i = alsadevs->cbegin(); i != alsadevs->cend(); ++i)
409 const QString& key = i.key();
410 const QString& desc = i.value();
411 QString devname = QString(
"ALSA:%1").arg(key);
424 QDir dev(
"/dev",
"dsp*", QDir::Name, QDir::System);
426 dev.setNameFilters(QStringList(
"adsp*"));
429 dev.setPath(
"/dev/sound");
432 dev.setNameFilters(QStringList(
"dsp*"));
434 dev.setNameFilters(QStringList(
"adsp*"));
441 QString name =
"JACK:";
442 QString desc = tr(
"Use JACK default sound server.");
457 for (QMap<QString, QString>::const_iterator i = devs->begin();
458 i != devs->end(); ++i)
460 QString key = i.key();
461 QString desc = i.value();
462 QString devname = QString(
"CoreAudio:%1").arg(key);
472 QString name =
"CoreAudio:Default Output Device";
473 QString desc = tr(
"CoreAudio default output");
484 QString name =
"Windows:";
485 QString desc =
"Windows default output";
495 if (!dxdevs->empty())
497 for (QMap<int, QString>::const_iterator i = dxdevs->begin();
498 i != dxdevs->end(); ++i)
500 QString devdesc = i.value();
501 QString devname = QString(
"DirectX:%1").arg(devdesc);
514#if CONFIG_AUDIO_PULSE
519#if CONFIG_AUDIO_PULSEOUTPUT
521 QString name =
"PulseAudio:default";
522 QString desc = tr(
"PulseAudio default sound server.");
534 QString name =
"OpenSLES:";
535 QString desc = tr(
"OpenSLES default output. Stereo support only.");
544 QString name =
"AudioTrack:";
545 QString desc = tr(
"Android AudioTrack output. Supports surround sound.");
555 QString name =
"NULL";
556 QString desc =
"NULL device";
567 uint8_t *buffer,
int &data_size,
570 bool got_frame =
false;
578 return AVERROR(ENOMEM);
592 int ret = avcodec_receive_frame(ctx,
m_frame);
595 if (ret == AVERROR(EAGAIN))
598 ret = avcodec_send_packet(ctx, pkt);
599 if (ret == AVERROR(EAGAIN))
604 LOG(VB_AUDIO, LOG_ERR,
LOC +
605 QString(
"audio decode error: %1 (%2)")
617 LOG(VB_AUDIO, LOG_DEBUG,
LOC +
618 QString(
"audio decode, no frame decoded (%1)").arg(ret));
622 auto format = (AVSampleFormat)
m_frame->format;
626 data_size =
m_frame->nb_samples *
m_frame->ch_layout.nb_channels * av_get_bytes_per_sample(format);
630 uint8_t* src =
nullptr;
632 if (av_sample_fmt_is_planar(format))
637 (
const uint8_t **)
m_frame->extended_data,
643 src =
m_frame->extended_data[0];
646 uint8_t* transit = buffer;
652 transit = (uint8_t*)av_malloc(data_size * av_get_bytes_per_sample(ctx->sample_fmt)
656 LOG(VB_AUDIO, LOG_ERR,
LOC +
657 QString(
"audio decode, out of memory"));
664 data_size = converter.
Process(transit, src, data_size,
true);
666 if (transit != buffer)
694 std::chrono::milliseconds timecode,
702 QMutexLocker locker(visual->mutex());
703 visual->add(buffer, b_len, timecode, chan, prec);
711 QMutexLocker locker(visual->mutex());
722 auto *samp16 = (int16_t*) frames;
723 auto *samp32 = (int32_t*) frames;
727 for(
int chn = 0 ; chn < channels; chn++)
733 static_cast<float>(0x03fffffff);
736 *samp16++ = qToLittleEndian<qint16>(ires >> 16);
738 *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)