[WASAPI] set stream audio category
[xbmc.git] / xbmc / cores / AudioEngine / Sinks / AESinkALSA.cpp
blob990583e36d8592710e1684d1a8897e49b1b04253
1 /*
2 * Copyright (C) 2010-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "AESinkALSA.h"
11 #include "ServiceBroker.h"
12 #include "cores/AudioEngine/AESinkFactory.h"
13 #include "cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h"
14 #ifndef HAVE_X11
15 #include "cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h"
16 #endif
17 #include "cores/AudioEngine/Utils/AEELDParser.h"
18 #include "cores/AudioEngine/Utils/AEUtil.h"
19 #include "platform/Platform.h"
20 #include "utils/XTimeUtils.h"
21 #include "utils/log.h"
23 #include <algorithm>
24 #include <limits.h>
25 #include <set>
26 #include <sstream>
27 #include <stdint.h>
28 #include <string>
30 #include <sys/utsname.h>
32 using namespace std::chrono_literals;
34 #define ALSA_OPTIONS (SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_RESAMPLE)
36 #define ALSA_MAX_CHANNELS 16
37 static enum AEChannel LegacyALSAChannelMap[ALSA_MAX_CHANNELS + 1] = {
38 AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
39 AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
40 AE_CH_NULL
43 static enum AEChannel LegacyALSAChannelMap51Wide[ALSA_MAX_CHANNELS + 1] = {
44 AE_CH_FL , AE_CH_FR , AE_CH_SL , AE_CH_SR , AE_CH_FC , AE_CH_LFE , AE_CH_BL , AE_CH_BR ,
45 AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
46 AE_CH_NULL
49 static enum AEChannel ALSAChannelMapPassthrough[ALSA_MAX_CHANNELS + 1] = {
50 AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW ,
51 AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
52 AE_CH_NULL
55 static unsigned int ALSASampleRateList[] =
57 5512,
58 8000,
59 11025,
60 16000,
61 22050,
62 32000,
63 44100,
64 48000,
65 64000,
66 88200,
67 96000,
68 176400,
69 192000,
70 384000,
74 namespace
76 struct SndConfigDeleter
78 void operator()(snd_config_t* p) { snd_config_delete(p); }
81 inline std::unique_ptr<snd_config_t, SndConfigDeleter> SndConfigCopy(snd_config_t* original)
83 snd_config_t* config;
84 snd_config_copy(&config, original);
85 return std::unique_ptr<snd_config_t, SndConfigDeleter>(config, SndConfigDeleter());
87 } // namespace
89 CAESinkALSA::CAESinkALSA() :
90 m_pcm(NULL)
92 /* ensure that ALSA has been initialized */
93 if (!snd_config)
94 snd_config_update();
97 CAESinkALSA::~CAESinkALSA()
99 Deinitialize();
102 void CAESinkALSA::Register()
104 AE::AESinkRegEntry entry;
105 entry.sinkName = "ALSA";
106 entry.createFunc = CAESinkALSA::Create;
107 entry.enumerateFunc = CAESinkALSA::EnumerateDevicesEx;
108 entry.cleanupFunc = CAESinkALSA::Cleanup;
109 AE::CAESinkFactory::RegisterSink(entry);
112 std::unique_ptr<IAESink> CAESinkALSA::Create(std::string& device, AEAudioFormat& desiredFormat)
114 auto sink = std::make_unique<CAESinkALSA>();
115 if (sink->Initialize(desiredFormat, device))
116 return sink;
118 return {};
121 inline CAEChannelInfo CAESinkALSA::GetChannelLayoutRaw(const AEAudioFormat& format)
123 unsigned int count = 0;
125 switch (format.m_streamInfo.m_type)
127 case CAEStreamInfo::STREAM_TYPE_DTSHD_MA:
128 case CAEStreamInfo::STREAM_TYPE_TRUEHD:
129 count = 8;
130 break;
131 case CAEStreamInfo::STREAM_TYPE_DTSHD_CORE:
132 case CAEStreamInfo::STREAM_TYPE_DTS_512:
133 case CAEStreamInfo::STREAM_TYPE_DTS_1024:
134 case CAEStreamInfo::STREAM_TYPE_DTS_2048:
135 case CAEStreamInfo::STREAM_TYPE_AC3:
136 case CAEStreamInfo::STREAM_TYPE_EAC3:
137 case CAEStreamInfo::STREAM_TYPE_DTSHD:
138 count = 2;
139 break;
140 default:
141 count = 0;
142 break;
145 CAEChannelInfo info;
146 for (unsigned int i = 0; i < count; ++i)
147 info += ALSAChannelMapPassthrough[i];
149 return info;
152 inline CAEChannelInfo CAESinkALSA::GetChannelLayoutLegacy(const AEAudioFormat& format, unsigned int minChannels, unsigned int maxChannels)
154 enum AEChannel* channelMap = LegacyALSAChannelMap;
155 unsigned int count = 0;
157 if (format.m_dataFormat == AE_FMT_RAW)
158 return GetChannelLayoutRaw(format);
160 // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
161 // but no BR BL channels, we use the wide map in order to open only the num of channels really
162 // needed.
163 if (format.m_channelLayout.HasChannel(AE_CH_SL) && !format.m_channelLayout.HasChannel(AE_CH_BL))
165 channelMap = LegacyALSAChannelMap51Wide;
167 for (unsigned int c = 0; c < 8; ++c)
169 for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
171 if (format.m_channelLayout[i] == channelMap[c])
173 count = c + 1;
174 break;
178 count = std::max(count, minChannels);
179 count = std::min(count, maxChannels);
181 CAEChannelInfo info;
182 for (unsigned int i = 0; i < count; ++i)
183 info += channelMap[i];
185 return info;
188 inline CAEChannelInfo CAESinkALSA::GetChannelLayout(const AEAudioFormat& format, unsigned int channels)
190 CAEChannelInfo info;
191 std::string alsaMapStr("none");
193 if (format.m_dataFormat == AE_FMT_RAW)
195 info = GetChannelLayoutRaw(format);
197 else
199 /* ask for the actual map */
200 snd_pcm_chmap_t* actualMap = snd_pcm_get_chmap(m_pcm);
201 if (actualMap)
203 alsaMapStr = ALSAchmapToString(actualMap);
205 info = ALSAchmapToAEChannelMap(actualMap);
207 /* "fake" a compatible map if it is more suitable for AE */
208 if (!info.ContainsChannels(format.m_channelLayout))
210 CAEChannelInfo infoAlternate = GetAlternateLayoutForm(info);
211 if (infoAlternate.Count())
213 std::vector<CAEChannelInfo> alts;
214 alts.push_back(info);
215 alts.push_back(infoAlternate);
216 if (format.m_channelLayout.BestMatch(alts) == 1)
217 info = infoAlternate;
221 /* add empty channels as needed (with e.g. FL,FR,LFE in 4ch) */
222 while (info.Count() < channels)
223 info += AE_CH_UNKNOWN1;
225 free(actualMap);
227 else
229 info = GetChannelLayoutLegacy(format, channels, channels);
233 CLog::Log(LOGDEBUG,
234 "CAESinkALSA::GetChannelLayout - Input Channel Count: {} Output Channel Count: {}",
235 format.m_channelLayout.Count(), info.Count());
236 CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Requested Layout: {}",
237 std::string(format.m_channelLayout));
238 CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Got Layout: {} (ALSA: {})",
239 std::string(info), alsaMapStr);
241 return info;
244 AEChannel CAESinkALSA::ALSAChannelToAEChannel(unsigned int alsaChannel)
246 AEChannel aeChannel;
247 switch (alsaChannel)
249 case SND_CHMAP_FL: aeChannel = AE_CH_FL; break;
250 case SND_CHMAP_FR: aeChannel = AE_CH_FR; break;
251 case SND_CHMAP_FC: aeChannel = AE_CH_FC; break;
252 case SND_CHMAP_LFE: aeChannel = AE_CH_LFE; break;
253 case SND_CHMAP_RL: aeChannel = AE_CH_BL; break;
254 case SND_CHMAP_RR: aeChannel = AE_CH_BR; break;
255 case SND_CHMAP_FLC: aeChannel = AE_CH_FLOC; break;
256 case SND_CHMAP_FRC: aeChannel = AE_CH_FROC; break;
257 case SND_CHMAP_RC: aeChannel = AE_CH_BC; break;
258 case SND_CHMAP_SL: aeChannel = AE_CH_SL; break;
259 case SND_CHMAP_SR: aeChannel = AE_CH_SR; break;
260 case SND_CHMAP_TFL: aeChannel = AE_CH_TFL; break;
261 case SND_CHMAP_TFR: aeChannel = AE_CH_TFR; break;
262 case SND_CHMAP_TFC: aeChannel = AE_CH_TFC; break;
263 case SND_CHMAP_TC: aeChannel = AE_CH_TC; break;
264 case SND_CHMAP_TRL: aeChannel = AE_CH_TBL; break;
265 case SND_CHMAP_TRR: aeChannel = AE_CH_TBR; break;
266 case SND_CHMAP_TRC: aeChannel = AE_CH_TBC; break;
267 case SND_CHMAP_RLC: aeChannel = AE_CH_BLOC; break;
268 case SND_CHMAP_RRC: aeChannel = AE_CH_BROC; break;
269 default: aeChannel = AE_CH_UNKNOWN1; break;
271 return aeChannel;
274 unsigned int CAESinkALSA::AEChannelToALSAChannel(AEChannel aeChannel)
276 unsigned int alsaChannel;
277 switch (aeChannel)
279 case AE_CH_FL: alsaChannel = SND_CHMAP_FL; break;
280 case AE_CH_FR: alsaChannel = SND_CHMAP_FR; break;
281 case AE_CH_FC: alsaChannel = SND_CHMAP_FC; break;
282 case AE_CH_LFE: alsaChannel = SND_CHMAP_LFE; break;
283 case AE_CH_BL: alsaChannel = SND_CHMAP_RL; break;
284 case AE_CH_BR: alsaChannel = SND_CHMAP_RR; break;
285 case AE_CH_FLOC: alsaChannel = SND_CHMAP_FLC; break;
286 case AE_CH_FROC: alsaChannel = SND_CHMAP_FRC; break;
287 case AE_CH_BC: alsaChannel = SND_CHMAP_RC; break;
288 case AE_CH_SL: alsaChannel = SND_CHMAP_SL; break;
289 case AE_CH_SR: alsaChannel = SND_CHMAP_SR; break;
290 case AE_CH_TFL: alsaChannel = SND_CHMAP_TFL; break;
291 case AE_CH_TFR: alsaChannel = SND_CHMAP_TFR; break;
292 case AE_CH_TFC: alsaChannel = SND_CHMAP_TFC; break;
293 case AE_CH_TC: alsaChannel = SND_CHMAP_TC; break;
294 case AE_CH_TBL: alsaChannel = SND_CHMAP_TRL; break;
295 case AE_CH_TBR: alsaChannel = SND_CHMAP_TRR; break;
296 case AE_CH_TBC: alsaChannel = SND_CHMAP_TRC; break;
297 case AE_CH_BLOC: alsaChannel = SND_CHMAP_RLC; break;
298 case AE_CH_BROC: alsaChannel = SND_CHMAP_RRC; break;
299 default: alsaChannel = SND_CHMAP_UNKNOWN; break;
301 return alsaChannel;
304 CAEChannelInfo CAESinkALSA::ALSAchmapToAEChannelMap(snd_pcm_chmap_t* alsaMap)
306 CAEChannelInfo info;
308 for (unsigned int i = 0; i < alsaMap->channels; i++)
309 info += ALSAChannelToAEChannel(alsaMap->pos[i]);
311 return info;
314 snd_pcm_chmap_t* CAESinkALSA::AEChannelMapToALSAchmap(const CAEChannelInfo& info)
316 int AECount = info.Count();
317 snd_pcm_chmap_t* alsaMap = (snd_pcm_chmap_t*)malloc(sizeof(snd_pcm_chmap_t) + AECount * sizeof(int));
319 alsaMap->channels = AECount;
321 for (int i = 0; i < AECount; i++)
322 alsaMap->pos[i] = AEChannelToALSAChannel(info[i]);
324 return alsaMap;
327 snd_pcm_chmap_t* CAESinkALSA::CopyALSAchmap(snd_pcm_chmap_t* alsaMap)
329 snd_pcm_chmap_t* copyMap = (snd_pcm_chmap_t*)malloc(sizeof(snd_pcm_chmap_t) + alsaMap->channels * sizeof(int));
331 copyMap->channels = alsaMap->channels;
332 memcpy(copyMap->pos, alsaMap->pos, alsaMap->channels * sizeof(int));
334 return copyMap;
337 std::string CAESinkALSA::ALSAchmapToString(snd_pcm_chmap_t* alsaMap)
339 char buf[128] = {};
340 //! @bug ALSA bug - buffer overflow by a factor of 2 is possible
341 //! http://mailman.alsa-project.org/pipermail/alsa-devel/2014-December/085815.html
342 int err = snd_pcm_chmap_print(alsaMap, sizeof(buf) / 2, buf);
343 if (err < 0)
344 return "Error";
345 return std::string(buf);
348 CAEChannelInfo CAESinkALSA::GetAlternateLayoutForm(const CAEChannelInfo& info)
350 CAEChannelInfo altLayout;
352 /* only handle symmetrical layouts */
353 if (info.HasChannel(AE_CH_BL) == info.HasChannel(AE_CH_BR) &&
354 info.HasChannel(AE_CH_SL) == info.HasChannel(AE_CH_SR) &&
355 info.HasChannel(AE_CH_BLOC) == info.HasChannel(AE_CH_BROC))
357 /* CEA-861-D used by HDMI 1.x has 7.1 as back+back-x-of-center, not
358 * side+back. Mangle it here. */
359 if (info.HasChannel(AE_CH_SL) && info.HasChannel(AE_CH_BL) && !info.HasChannel(AE_CH_BLOC))
361 altLayout = info;
362 altLayout.ReplaceChannel(AE_CH_BL, AE_CH_BLOC);
363 altLayout.ReplaceChannel(AE_CH_BR, AE_CH_BROC);
364 altLayout.ReplaceChannel(AE_CH_SL, AE_CH_BL);
365 altLayout.ReplaceChannel(AE_CH_SR, AE_CH_BR);
367 /* same in reverse */
368 else if (!info.HasChannel(AE_CH_SL) && info.HasChannel(AE_CH_BL) && info.HasChannel(AE_CH_BLOC))
370 altLayout = info;
371 altLayout.ReplaceChannel(AE_CH_BL, AE_CH_SL);
372 altLayout.ReplaceChannel(AE_CH_BR, AE_CH_SR);
373 altLayout.ReplaceChannel(AE_CH_BLOC, AE_CH_BL);
374 altLayout.ReplaceChannel(AE_CH_BROC, AE_CH_BR);
376 /* We have side speakers but no back speakers, allow map to back
377 * speakers. */
378 else if (info.HasChannel(AE_CH_SL) && !info.HasChannel(AE_CH_BL))
380 altLayout = info;
381 altLayout.ReplaceChannel(AE_CH_SL, AE_CH_BL);
382 altLayout.ReplaceChannel(AE_CH_SR, AE_CH_BR);
384 /* reverse */
385 else if (!info.HasChannel(AE_CH_SL) && info.HasChannel(AE_CH_BL))
387 altLayout = info;
388 altLayout.ReplaceChannel(AE_CH_BL, AE_CH_SL);
389 altLayout.ReplaceChannel(AE_CH_BR, AE_CH_SR);
392 return altLayout;
395 snd_pcm_chmap_t* CAESinkALSA::SelectALSAChannelMap(const CAEChannelInfo& info)
397 snd_pcm_chmap_t* chmap = NULL;
398 snd_pcm_chmap_query_t** supportedMaps;
400 supportedMaps = snd_pcm_query_chmaps(m_pcm);
402 if (!supportedMaps)
403 return NULL;
405 CAEChannelInfo infoAlternate = GetAlternateLayoutForm(info);
407 /* for efficiency, first try to find an exact match, and only then fallback
408 * to searching for less perfect matches */
409 int i = 0;
410 for (snd_pcm_chmap_query_t* supportedMap = supportedMaps[i++];
411 supportedMap; supportedMap = supportedMaps[i++])
413 if (supportedMap->map.channels == info.Count())
415 CAEChannelInfo candidate = ALSAchmapToAEChannelMap(&supportedMap->map);
416 const CAEChannelInfo* selectedInfo = &info;
418 if (!candidate.ContainsChannels(info) || !info.ContainsChannels(candidate))
420 selectedInfo = &infoAlternate;
421 if (!candidate.ContainsChannels(infoAlternate) || !infoAlternate.ContainsChannels(candidate))
422 continue;
425 if (supportedMap->type == SND_CHMAP_TYPE_VAR)
427 /* device supports the AE map directly */
428 chmap = AEChannelMapToALSAchmap(*selectedInfo);
429 break;
431 else
433 /* device needs 1:1 remapping */
434 chmap = CopyALSAchmap(&supportedMap->map);
435 break;
440 /* if no exact chmap was found, fallback to best-effort */
441 if (!chmap)
443 CAEChannelInfo allChannels;
444 std::vector<CAEChannelInfo> supportedMapsAE;
446 /* Convert the ALSA maps to AE maps. */
447 int i = 0;
448 for (snd_pcm_chmap_query_t* supportedMap = supportedMaps[i++];
449 supportedMap; supportedMap = supportedMaps[i++])
450 supportedMapsAE.push_back(ALSAchmapToAEChannelMap(&supportedMap->map));
452 int score = 0;
453 int best = info.BestMatch(supportedMapsAE, &score);
455 /* see if we find a better result with the alternate form */
456 if (infoAlternate.Count() && score < 0)
458 int scoreAlt = 0;
459 int bestAlt = infoAlternate.BestMatch(supportedMapsAE, &scoreAlt);
460 if (scoreAlt > score)
461 best = bestAlt;
464 if (best > 0)
465 chmap = CopyALSAchmap(&supportedMaps[best]->map);
468 if (chmap && CServiceBroker::GetLogging().CanLogComponent(LOGAUDIO))
469 CLog::Log(LOGDEBUG, "CAESinkALSA::SelectALSAChannelMap - Selected ALSA map \"{}\"",
470 ALSAchmapToString(chmap));
472 snd_pcm_free_chmaps(supportedMaps);
473 return chmap;
476 void CAESinkALSA::GetAESParams(const AEAudioFormat& format, std::string& params)
478 if (m_passthrough)
479 params = "AES0=0x06";
480 else
481 params = "AES0=0x04";
483 params += ",AES1=0x82,AES2=0x00";
485 if (m_passthrough && format.m_channelLayout.Count() == 8) params += ",AES3=0x09";
486 else if (format.m_sampleRate == 192000) params += ",AES3=0x0e";
487 else if (format.m_sampleRate == 176400) params += ",AES3=0x0c";
488 else if (format.m_sampleRate == 96000) params += ",AES3=0x0a";
489 else if (format.m_sampleRate == 88200) params += ",AES3=0x08";
490 else if (format.m_sampleRate == 48000) params += ",AES3=0x02";
491 else if (format.m_sampleRate == 44100) params += ",AES3=0x00";
492 else if (format.m_sampleRate == 32000) params += ",AES3=0x03";
493 else params += ",AES3=0x01";
496 bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
498 m_initDevice = device;
499 m_initFormat = format;
500 ALSAConfig inconfig, outconfig;
501 inconfig.format = format.m_dataFormat;
502 inconfig.sampleRate = format.m_sampleRate;
505 * We can't use the better GetChannelLayout() at this point as the device
506 * is not opened yet, and we need inconfig.channels to select the correct
507 * device... Legacy layouts should be accurate enough for device selection
508 * in all cases, though.
510 inconfig.channels = GetChannelLayoutLegacy(format, 2, 8).Count();
512 /* if we are raw, correct the data format */
513 if (format.m_dataFormat == AE_FMT_RAW)
515 inconfig.format = AE_FMT_S16NE;
516 m_passthrough = true;
518 else
520 m_passthrough = false;
523 if (inconfig.channels == 0)
525 CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
526 return false;
529 AEDeviceType devType = AEDeviceTypeFromName(device);
531 std::string AESParams;
532 /* digital interfaces should have AESx set, though in practice most
533 * receivers don't care */
534 if (m_passthrough || devType == AE_DEVTYPE_HDMI || devType == AE_DEVTYPE_IEC958)
535 GetAESParams(format, AESParams);
537 CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device \"{}\"", device);
539 /* get the sound config */
540 std::unique_ptr<snd_config_t, SndConfigDeleter> config = SndConfigCopy(snd_config);
542 if (!OpenPCMDevice(device, AESParams, inconfig.channels, &m_pcm, config.get()))
544 CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"{}\"", device);
545 return false;
548 /* get the actual device name that was used */
549 device = snd_pcm_name(m_pcm);
550 m_device = device;
552 CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Opened device \"{}\"", device);
554 snd_pcm_chmap_t* selectedChmap = NULL;
555 if (!m_passthrough)
557 selectedChmap = SelectALSAChannelMap(format.m_channelLayout);
558 if (selectedChmap)
560 /* update wanted channel count according to the selected map */
561 inconfig.channels = selectedChmap->channels;
565 if (!InitializeHW(inconfig, outconfig) || !InitializeSW(outconfig))
567 free(selectedChmap);
568 return false;
571 if (selectedChmap)
573 /* failure is OK, that likely just means the selected chmap is fixed already */
574 snd_pcm_set_chmap(m_pcm, selectedChmap);
575 free(selectedChmap);
578 // we want it blocking
579 snd_pcm_nonblock(m_pcm, 0);
580 snd_pcm_prepare (m_pcm);
582 if (m_passthrough && inconfig.channels != outconfig.channels)
584 CLog::Log(LOGINFO, "CAESinkALSA::Initialize - could not open required number of channels");
585 return false;
587 // adjust format to the configuration we got
588 format.m_channelLayout = GetChannelLayout(format, outconfig.channels);
589 // we might end up with an unusable channel layout that contains only UNKNOWN
590 // channels, let's do a sanity check.
591 if (!format.m_channelLayout.IsLayoutValid())
592 return false;
594 format.m_sampleRate = outconfig.sampleRate;
595 format.m_frames = outconfig.periodSize;
596 format.m_frameSize = outconfig.frameSize;
597 format.m_dataFormat = outconfig.format;
599 m_format = format;
600 m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate;
602 return true;
605 snd_pcm_format_t CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat format)
607 if (format == AE_FMT_RAW)
608 return SND_PCM_FORMAT_S16;
610 switch (format)
612 case AE_FMT_U8 : return SND_PCM_FORMAT_U8;
613 case AE_FMT_S16NE : return SND_PCM_FORMAT_S16;
614 case AE_FMT_S16LE : return SND_PCM_FORMAT_S16_LE;
615 case AE_FMT_S16BE : return SND_PCM_FORMAT_S16_BE;
616 case AE_FMT_S24NE4: return SND_PCM_FORMAT_S24;
617 #ifdef __BIG_ENDIAN__
618 case AE_FMT_S24NE3: return SND_PCM_FORMAT_S24_3BE;
619 #else
620 case AE_FMT_S24NE3: return SND_PCM_FORMAT_S24_3LE;
621 #endif
622 case AE_FMT_S32NE : return SND_PCM_FORMAT_S32;
623 case AE_FMT_FLOAT : return SND_PCM_FORMAT_FLOAT;
625 default:
626 return SND_PCM_FORMAT_UNKNOWN;
630 bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig)
632 snd_pcm_hw_params_t *hw_params;
634 snd_pcm_hw_params_alloca(&hw_params);
635 memset(hw_params, 0, snd_pcm_hw_params_sizeof());
637 snd_pcm_hw_params_any(m_pcm, hw_params);
638 snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
640 unsigned int sampleRate = inconfig.sampleRate;
641 snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL);
643 unsigned int channelCount = inconfig.channels;
644 /* select a channel count >=wanted, or otherwise the highest available */
645 if (snd_pcm_hw_params_set_channels_min(m_pcm, hw_params, &channelCount) == 0)
646 snd_pcm_hw_params_set_channels_first(m_pcm, hw_params, &channelCount);
647 else
648 snd_pcm_hw_params_set_channels_last(m_pcm, hw_params, &channelCount);
650 /* ensure we opened X channels or more */
651 if (inconfig.channels > channelCount)
653 CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels");
656 /* update outconfig */
657 outconfig.channels = channelCount;
659 snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format);
660 outconfig.format = inconfig.format;
662 if (fmt == SND_PCM_FORMAT_UNKNOWN)
664 /* if we dont support the requested format, fallback to float */
665 fmt = SND_PCM_FORMAT_FLOAT;
666 outconfig.format = AE_FMT_FLOAT;
669 snd_pcm_hw_params_t *hw_params_copy;
670 snd_pcm_hw_params_alloca(&hw_params_copy);
671 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have
673 /* try the data format */
674 if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
676 /* if the chosen format is not supported, try each one in descending order */
677 CLog::Log(LOGINFO,
678 "CAESinkALSA::InitializeHW - Your hardware does not support {}, trying other formats",
679 CAEUtil::DataFormatToStr(outconfig.format));
680 for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
682 if (i == AE_FMT_RAW || i == AE_FMT_MAX)
683 continue;
685 if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE)
686 continue;
688 fmt = AEFormatToALSAFormat(i);
689 if (fmt == SND_PCM_FORMAT_UNKNOWN)
690 continue;
692 snd_pcm_hw_params_copy(hw_params, hw_params_copy); // restore from copy
693 if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
695 fmt = SND_PCM_FORMAT_UNKNOWN;
696 continue;
699 int fmtBits = CAEUtil::DataFormatToBits(i);
700 int bits = snd_pcm_hw_params_get_sbits(hw_params);
702 // skip bits check when alsa reports invalid sbits value
703 if (bits > 0 && bits != fmtBits)
705 /* if we opened in 32bit and only have 24bits, signal it accordingly */
706 if (fmt == SND_PCM_FORMAT_S32 && bits == 24)
707 i = AE_FMT_S24NE4MSB;
708 else if (fmt == SND_PCM_FORMAT_S24 && bits == 24)
709 i = AE_FMT_S24NE4;
710 else
711 continue;
714 /* record that the format fell back to X */
715 outconfig.format = i;
716 CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format {}",
717 CAEUtil::DataFormatToStr(outconfig.format));
718 break;
721 /* if we failed to find a valid output format */
722 if (fmt == SND_PCM_FORMAT_UNKNOWN)
724 CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format");
725 return false;
729 snd_pcm_uframes_t periodSize, bufferSize;
730 snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);
731 snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL);
734 We want to make sure, that we have max 200 ms Buffer with
735 a periodSize of approx 50 ms. Choosing a higher bufferSize
736 will cause problems with menu sounds. Buffer will be increased
737 after those are fixed.
739 periodSize = std::min(periodSize, (snd_pcm_uframes_t) sampleRate / 20);
740 bufferSize = std::min(bufferSize, (snd_pcm_uframes_t) sampleRate / 5);
743 According to upstream we should set buffer size first - so make sure it is always at least
744 4x period size to not get underruns (some systems seem to have issues with only 2 periods)
746 periodSize = std::min(periodSize, bufferSize / 4);
748 CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize {}, bufferSize {}",
749 periodSize, bufferSize);
751 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have and is already working
753 // Make sure to not initialize too large to not cause underruns
754 snd_pcm_uframes_t periodSizeMax = bufferSize / 3;
755 if(snd_pcm_hw_params_set_period_size_max(m_pcm, hw_params_copy, &periodSizeMax, NULL) != 0)
757 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
758 CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: Failed to limit periodSize to {}",
759 periodSizeMax);
762 // first trying bufferSize, PeriodSize
763 // for more info see here:
764 // http://mailman.alsa-project.org/pipermail/alsa-devel/2009-September/021069.html
765 // the last three tries are done as within pulseaudio
767 // backup periodSize and bufferSize first. Restore them after every failed try
768 snd_pcm_uframes_t periodSizeTemp, bufferSizeTemp;
769 periodSizeTemp = periodSize;
770 bufferSizeTemp = bufferSize;
771 if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
772 || snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
773 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
775 bufferSize = bufferSizeTemp;
776 periodSize = periodSizeTemp;
777 // retry with PeriodSize, bufferSize
778 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
779 if (snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
780 || snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
781 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
783 // try only periodSize
784 periodSize = periodSizeTemp;
785 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
786 if(snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
787 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
789 // try only BufferSize
790 bufferSize = bufferSizeTemp;
791 snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
792 if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
793 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
795 // set default that Alsa would choose
796 CLog::Log(LOGWARNING, "CAESinkAlsa::InitializeHW - Using default alsa values - set failed");
797 if (snd_pcm_hw_params(m_pcm, hw_params) != 0)
799 CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Could not init a valid sink");
800 return false;
804 // reread values when alsa default was kept
805 snd_pcm_get_params(m_pcm, &bufferSize, &periodSize);
809 CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize {}, bufferSize {}", periodSize,
810 bufferSize);
812 /* set the format parameters */
813 outconfig.sampleRate = sampleRate;
815 /* if periodSize is too small Audio Engine might starve */
816 m_fragmented = false;
817 unsigned int fragments = 1;
818 if (periodSize < AE_MIN_PERIODSIZE)
820 fragments = std::ceil((double) AE_MIN_PERIODSIZE / periodSize);
821 CLog::Log(LOGDEBUG, "Audio Driver reports too low periodSize {} - will use {} fragments",
822 (int)periodSize, (int)fragments);
823 m_fragmented = true;
826 m_originalPeriodSize = periodSize;
827 outconfig.periodSize = fragments * periodSize;
828 outconfig.frameSize = snd_pcm_frames_to_bytes(m_pcm, 1);
830 m_bufferSize = (unsigned int)bufferSize;
831 m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate);
833 CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to {} ms", m_timeout);
835 return true;
838 bool CAESinkALSA::InitializeSW(const ALSAConfig &inconfig)
840 snd_pcm_sw_params_t *sw_params;
841 snd_pcm_uframes_t boundary;
843 snd_pcm_sw_params_alloca(&sw_params);
844 memset(sw_params, 0, snd_pcm_sw_params_sizeof());
846 snd_pcm_sw_params_current (m_pcm, sw_params);
847 snd_pcm_sw_params_set_start_threshold (m_pcm, sw_params, INT_MAX);
848 snd_pcm_sw_params_set_silence_threshold(m_pcm, sw_params, 0);
849 snd_pcm_sw_params_get_boundary (sw_params, &boundary);
850 snd_pcm_sw_params_set_silence_size (m_pcm, sw_params, boundary);
851 snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, inconfig.periodSize);
853 if (snd_pcm_sw_params(m_pcm, sw_params) < 0)
855 CLog::Log(LOGERROR, "CAESinkALSA::InitializeSW - Failed to set the parameters");
856 return false;
859 return true;
862 void CAESinkALSA::Deinitialize()
864 if (m_pcm)
866 Stop();
867 snd_pcm_close(m_pcm);
868 m_pcm = NULL;
872 void CAESinkALSA::Stop()
874 if (!m_pcm)
875 return;
876 snd_pcm_drop(m_pcm);
879 void CAESinkALSA::GetDelay(AEDelayStatus& status)
881 if (!m_pcm)
883 status.SetDelay(0);
884 return;
886 snd_pcm_sframes_t frames = 0;
887 snd_pcm_delay(m_pcm, &frames);
889 if (frames < 0)
891 snd_pcm_forward(m_pcm, -frames);
892 frames = 0;
895 status.SetDelay((double)frames * m_formatSampleRateMul);
898 double CAESinkALSA::GetCacheTotal()
900 return (double)m_bufferSize * m_formatSampleRateMul;
903 unsigned int CAESinkALSA::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
905 if (!m_pcm)
907 CLog::Log(LOGERROR, "CAESinkALSA - Tried to add packets without a sink");
908 return INT_MAX;
911 void *buffer = data[0]+offset*m_format.m_frameSize;
912 unsigned int amount = 0;
913 int64_t data_left = (int64_t) frames;
914 int frames_written = 0;
916 while (data_left > 0)
918 if (m_fragmented)
919 amount = std::min((unsigned int) data_left, m_originalPeriodSize);
920 else // take care as we can come here a second time if the sink does not eat all data
921 amount = (unsigned int) data_left;
923 int ret = snd_pcm_writei(m_pcm, buffer, amount);
924 if (ret < 0)
926 CLog::Log(LOGERROR, "CAESinkALSA - snd_pcm_writei({}) {} - trying to recover", ret,
927 snd_strerror(ret));
928 ret = snd_pcm_recover(m_pcm, ret, 1);
929 if(ret < 0)
931 HandleError("snd_pcm_writei(1)", ret);
932 ret = snd_pcm_writei(m_pcm, buffer, amount);
933 if (ret < 0)
935 HandleError("snd_pcm_writei(2)", ret);
936 ret = 0;
941 if ( ret > 0 && snd_pcm_state(m_pcm) == SND_PCM_STATE_PREPARED)
942 snd_pcm_start(m_pcm);
944 if (ret <= 0)
945 break;
947 frames_written += ret;
948 data_left -= ret;
949 buffer = data[0]+offset*m_format.m_frameSize + frames_written*m_format.m_frameSize;
951 return frames_written;
954 void CAESinkALSA::HandleError(const char* name, int err)
956 switch(err)
958 case -EPIPE:
959 CLog::Log(LOGERROR, "CAESinkALSA::HandleError({}) - underrun", name);
960 if ((err = snd_pcm_prepare(m_pcm)) < 0)
961 CLog::Log(LOGERROR, "CAESinkALSA::HandleError({}) - snd_pcm_prepare returned {} ({})", name,
962 err, snd_strerror(err));
963 break;
965 case -ESTRPIPE:
966 CLog::Log(LOGINFO, "CAESinkALSA::HandleError({}) - Resuming after suspend", name);
968 /* try to resume the stream */
969 while((err = snd_pcm_resume(m_pcm)) == -EAGAIN)
970 KODI::TIME::Sleep(1ms);
972 /* if the hardware doesn't support resume, prepare the stream */
973 if (err == -ENOSYS)
974 if ((err = snd_pcm_prepare(m_pcm)) < 0)
975 CLog::Log(LOGERROR, "CAESinkALSA::HandleError({}) - snd_pcm_prepare returned {} ({})",
976 name, err, snd_strerror(err));
977 break;
979 default:
980 CLog::Log(LOGERROR, "CAESinkALSA::HandleError({}) - snd_pcm_writei returned {} ({})", name,
981 err, snd_strerror(err));
982 break;
986 void CAESinkALSA::Drain()
988 if (!m_pcm)
989 return;
991 snd_pcm_drain(m_pcm);
992 snd_pcm_prepare(m_pcm);
995 void CAESinkALSA::AppendParams(std::string &device, const std::string &params)
997 /* Note: escaping, e.g. "plug:'something:X=y'" isn't handled,
998 * but it is not normally encountered at this point. */
1000 device += (device.find(':') == std::string::npos) ? ':' : ',';
1001 device += params;
1004 bool CAESinkALSA::TryDevice(const std::string &name, snd_pcm_t **pcmp, snd_config_t *lconf)
1006 /* Check if this device was already open (e.g. when checking for supported
1007 * channel count in EnumerateDevice()) */
1008 if (*pcmp)
1010 if (name == snd_pcm_name(*pcmp))
1011 return true;
1013 snd_pcm_close(*pcmp);
1014 *pcmp = NULL;
1017 int err = snd_pcm_open_lconf(pcmp, name.c_str(), SND_PCM_STREAM_PLAYBACK, ALSA_OPTIONS, lconf);
1018 if (err < 0)
1020 CLog::Log(LOGINFO, "CAESinkALSA - Unable to open device \"{}\" for playback", name);
1023 return err == 0;
1026 bool CAESinkALSA::TryDeviceWithParams(const std::string &name, const std::string &params, snd_pcm_t **pcmp, snd_config_t *lconf)
1028 if (!params.empty())
1030 std::string nameWithParams = name;
1031 AppendParams(nameWithParams, params);
1032 if (TryDevice(nameWithParams, pcmp, lconf))
1033 return true;
1036 /* Try the variant without extra parameters.
1037 * Custom devices often do not take the AESx parameters, for example.
1039 return TryDevice(name, pcmp, lconf);
1042 bool CAESinkALSA::OpenPCMDevice(const std::string &name, const std::string &params, int channels, snd_pcm_t **pcmp, snd_config_t *lconf)
1044 /* Special name denoting surroundXX mangling. This is needed for some
1045 * devices for multichannel to work. */
1046 if (name == "@" || name.substr(0, 2) == "@:")
1048 std::string openName = name.substr(1);
1050 /* These device names allow alsa-lib to perform special routing if needed
1051 * for multichannel to work with the audio hardware.
1052 * Fall through in switch() so that devices with more channels are
1053 * added as fallback. */
1054 switch (channels)
1056 case 3:
1057 case 4:
1058 if (TryDeviceWithParams("surround40" + openName, params, pcmp, lconf))
1059 return true;
1060 [[fallthrough]];
1061 case 5:
1062 case 6:
1063 if (TryDeviceWithParams("surround51" + openName, params, pcmp, lconf))
1064 return true;
1065 [[fallthrough]];
1066 case 7:
1067 case 8:
1068 if (TryDeviceWithParams("surround71" + openName, params, pcmp, lconf))
1069 return true;
1072 /* Try "sysdefault" and "default" (they provide dmix if needed, and route
1073 * audio to all extra channels on subdeviced cards),
1074 * unless the selected devices is not DEV=0 of the card, in which case
1075 * "sysdefault" and "default" would point to another device.
1076 * "sysdefault" is a newish device name that won't be overwritten in case
1077 * system configuration redefines "default". "default" is still tried
1078 * because "sysdefault" is rather new. */
1079 size_t devPos = openName.find(",DEV=");
1080 if (devPos == std::string::npos || (devPos + 5 < openName.size() && openName[devPos+5] == '0'))
1082 /* "sysdefault" and "default" do not have "DEV=0", drop it */
1083 std::string nameWithoutDev = openName;
1084 if (devPos != std::string::npos)
1085 nameWithoutDev.erase(nameWithoutDev.begin() + devPos, nameWithoutDev.begin() + devPos + 6);
1087 if (TryDeviceWithParams("sysdefault" + nameWithoutDev, params, pcmp, lconf)
1088 || TryDeviceWithParams("default" + nameWithoutDev, params, pcmp, lconf))
1089 return true;
1092 /* Try "front" (no dmix, no audio in other channels on subdeviced cards) */
1093 if (TryDeviceWithParams("front" + openName, params, pcmp, lconf))
1094 return true;
1097 else
1099 /* Non-surroundXX device, just add it */
1100 if (TryDeviceWithParams(name, params, pcmp, lconf))
1101 return true;
1104 return false;
1107 void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
1109 #if defined(HAVE_LIBUDEV)
1110 const auto deviceMonitor = CServiceBroker::GetPlatform().GetService<CALSADeviceMonitor>();
1111 deviceMonitor->Start();
1112 #endif
1114 /* ensure that ALSA has been initialized */
1115 snd_lib_error_set_handler(sndLibErrorHandler);
1116 if(!snd_config || force)
1118 if(force)
1119 snd_config_update_free_global();
1121 snd_config_update();
1124 std::unique_ptr<snd_config_t, SndConfigDeleter> config = SndConfigCopy(snd_config);
1126 #if !defined(HAVE_X11)
1127 const auto controlMonitor = CServiceBroker::GetPlatform().GetService<CALSAHControlMonitor>();
1128 controlMonitor->Clear();
1129 #endif
1131 /* Always enumerate the default device.
1132 * Note: If "default" is a stereo device, EnumerateDevice()
1133 * will automatically add "@" instead to enable surroundXX mangling.
1134 * We don't want to do that if "default" can handle multichannel
1135 * itself (e.g. in case of a pulseaudio server). */
1136 EnumerateDevice(list, "default", "", config.get());
1138 void **hints;
1140 if (snd_device_name_hint(-1, "pcm", &hints) < 0)
1142 CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices");
1143 return;
1146 std::string defaultDescription;
1148 for (void** hint = hints; *hint != NULL; ++hint)
1150 char *io = snd_device_name_get_hint(*hint, "IOID");
1151 char *name = snd_device_name_get_hint(*hint, "NAME");
1152 char *desc = snd_device_name_get_hint(*hint, "DESC");
1153 if ((!io || strcmp(io, "Output") == 0) && name
1154 && strcmp(name, "null") != 0)
1156 std::string baseName = std::string(name);
1157 baseName = baseName.substr(0, baseName.find(':'));
1159 if (strcmp(name, "default") == 0)
1161 /* added already, but lets get the description if we have one */
1162 if (desc)
1163 defaultDescription = desc;
1165 else if (baseName == "front")
1167 /* Enumerate using the surroundXX mangling */
1168 /* do not enumerate basic "front", it is already handled
1169 * by the default "@" entry added in the very beginning */
1170 if (strcmp(name, "front") != 0)
1171 EnumerateDevice(list, std::string("@") + (name + 5), desc ? desc : name, config.get());
1174 /* Do not enumerate "default", it is already enumerated above. */
1176 /* Do not enumerate the surroundXX devices, those are always accompanied
1177 * with a "front" device and it is handled above as "@". The below
1178 * devices plus sysdefault will be automatically used if available
1179 * for a "@" device.
1180 * sysdefault devices are enumerated as not all cards have front/surround
1181 * devices. For cards with front/surround devices the sysdefault
1182 * entry will be removed in a second pass after enumeration.
1185 /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended"
1186 * defaults to "on" instead of upstream "off", causing lots of unwanted
1187 * extra devices (many of which are not actually routed properly) to be
1188 * found by the enumeration process. Skip them as well ("hw", "dmix",
1189 * "plughw", "dsnoop"). */
1191 else if (baseName != "default"
1192 && baseName != "surround40"
1193 && baseName != "surround41"
1194 && baseName != "surround50"
1195 && baseName != "surround51"
1196 && baseName != "surround71"
1197 && baseName != "hw"
1198 && baseName != "dmix"
1199 && baseName != "plughw"
1200 && baseName != "dsnoop")
1202 EnumerateDevice(list, name, desc ? desc : name, config.get());
1205 free(io);
1206 free(name);
1207 free(desc);
1209 snd_device_name_free_hint(hints);
1211 #if !defined(HAVE_X11)
1212 controlMonitor->Start();
1213 #endif
1215 /* set the displayname for default device */
1216 if (!list.empty() && list[0].m_deviceName == "default")
1218 /* If we have one from a hint (DESC), use it */
1219 if (!defaultDescription.empty())
1220 list[0].m_displayName = defaultDescription;
1221 /* Otherwise use the discovered name or (unlikely) "Default" */
1222 else if (list[0].m_displayName.empty())
1223 list[0].m_displayName = "Default";
1226 /* cards with surround entries where sysdefault should be removed */
1227 std::set<std::string> cardsWithSurround;
1229 for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1)
1231 std::string baseName = it1->m_deviceName.substr(0, it1->m_deviceName.find(':'));
1232 std::string card = GetParamFromName(it1->m_deviceName, "CARD");
1233 if (baseName == "@" && !card.empty())
1234 cardsWithSurround.insert(card);
1237 if (!cardsWithSurround.empty())
1239 /* remove sysdefault entries where we already have a surround entry */
1240 AEDeviceInfoList::iterator iter = list.begin();
1241 while (iter != list.end())
1243 std::string baseName = iter->m_deviceName.substr(0, iter->m_deviceName.find(':'));
1244 std::string card = GetParamFromName(iter->m_deviceName, "CARD");
1245 if (baseName == "sysdefault" && cardsWithSurround.find(card) != cardsWithSurround.end())
1246 iter = list.erase(iter);
1247 else
1248 ++iter;
1252 /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */
1253 /* If even a single device of card/dev X clashes with Y, add suffixes to
1254 * all devices of both them, for clarity. */
1256 /* clashing card names, e.g. "NVidia", "NVidia_2" */
1257 std::set<std::string> cardsToAppend;
1259 /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */
1260 std::set<std::pair<std::string, std::string> > devsToAppend;
1262 for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1)
1264 bool replaceName = false;
1266 for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2)
1268 if (it1->m_displayName == it2->m_displayName
1269 && it1->m_displayNameExtra == it2->m_displayNameExtra)
1271 /* something needs to be done */
1272 std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD");
1273 std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD");
1275 if (cardString1 != cardString2)
1277 /* card name differs, add identifiers to all devices */
1278 cardsToAppend.insert(cardString1);
1279 cardsToAppend.insert(cardString2);
1280 continue;
1283 std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV");
1284 std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV");
1286 if (devString1 != devString2)
1288 /* device number differs, add identifiers to all such devices */
1289 devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1));
1290 devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2));
1291 continue;
1294 /* if we got here, the configuration is really weird, just append the whole device string */
1295 replaceName = true;
1296 it2->m_displayName += " (" + it2->m_deviceName + ")";
1300 if (replaceName)
1301 it1->m_displayName = it1->m_displayName + " (" + it1->m_deviceName + ")";
1304 for (std::set<std::string>::iterator it = cardsToAppend.begin();
1305 it != cardsToAppend.end(); ++it)
1307 for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
1309 std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
1310 if (cardString == *it)
1311 /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */
1312 itl->m_displayName += " (" + cardString + ")";
1316 for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin();
1317 it != devsToAppend.end(); ++it)
1319 for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
1321 std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':'));
1322 std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
1323 if (baseName == it->first && cardString == it->second)
1325 std::string devString = GetParamFromName(itl->m_deviceName, "DEV");
1326 /* "HDMI #0", "HDMI #1" ... */
1327 itl->m_displayNameExtra += " #" + devString;
1333 AEDeviceType CAESinkALSA::AEDeviceTypeFromName(const std::string &name)
1335 if (name.substr(0, 4) == "hdmi")
1336 return AE_DEVTYPE_HDMI;
1337 else if (name.substr(0, 6) == "iec958" || name.substr(0, 5) == "spdif")
1338 return AE_DEVTYPE_IEC958;
1340 return AE_DEVTYPE_PCM;
1343 std::string CAESinkALSA::GetParamFromName(const std::string &name, const std::string &param)
1345 /* name = "hdmi:CARD=x,DEV=y" param = "CARD" => return "x" */
1346 size_t parPos = name.find(param + '=');
1347 if (parPos != std::string::npos)
1349 parPos += param.size() + 1;
1350 return name.substr(parPos, name.find_first_of(",'\"", parPos)-parPos);
1353 return "";
1356 void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config)
1358 snd_pcm_t *pcmhandle = NULL;
1359 if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config))
1360 return;
1362 snd_pcm_info_t *pcminfo;
1363 snd_pcm_info_alloca(&pcminfo);
1364 memset(pcminfo, 0, snd_pcm_info_sizeof());
1366 int err = snd_pcm_info(pcmhandle, pcminfo);
1367 if (err < 0)
1369 CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"{}\"", device);
1370 snd_pcm_close(pcmhandle);
1373 int cardNr = snd_pcm_info_get_card(pcminfo);
1375 CAEDeviceInfo info;
1376 info.m_deviceName = device;
1377 info.m_deviceType = AEDeviceTypeFromName(device);
1379 if (cardNr >= 0)
1381 /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */
1382 char *cardName;
1383 if (snd_card_get_name(cardNr, &cardName) == 0)
1385 info.m_displayName = cardName;
1386 free(cardName);
1389 if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 &&
1390 info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI")
1392 /* We already know this is HDMI, strip it */
1393 info.m_displayName.erase(info.m_displayName.size()-5);
1396 /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */
1397 std::string pcminfoName = snd_pcm_info_get_name(pcminfo);
1400 * Filter "USB Audio", in those cases snd_card_get_name() is more
1401 * meaningful already
1403 if (pcminfoName != "USB Audio")
1404 info.m_displayNameExtra = pcminfoName;
1406 if (info.m_deviceType == AE_DEVTYPE_HDMI)
1408 /* replace, this was likely "HDMI 0" */
1409 info.m_displayNameExtra = "HDMI";
1411 int dev = snd_pcm_info_get_device(pcminfo);
1413 if (dev >= 0)
1415 /* lets see if we can get ELD info */
1417 snd_ctl_t *ctlhandle;
1418 std::stringstream sstr;
1419 sstr << "hw:" << cardNr;
1420 std::string strHwName = sstr.str();
1422 if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0)
1424 snd_hctl_t *hctl;
1425 if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0)
1427 snd_hctl_load(hctl);
1428 bool badHDMI = false;
1430 #if !defined(HAVE_X11)
1431 /* add ELD to monitoring */
1432 const auto controlMonitor =
1433 CServiceBroker::GetPlatform().GetService<CALSAHControlMonitor>();
1434 controlMonitor->Add(strHwName, SND_CTL_ELEM_IFACE_PCM, dev, "ELD");
1435 #endif
1437 if (!GetELD(hctl, dev, info, badHDMI))
1438 CLog::Log(LOGDEBUG,
1439 "CAESinkALSA - Unable to obtain ELD information for device \"{}\" (not "
1440 "supported by device, or kernel older than 3.2)",
1441 device);
1443 /* snd_hctl_close also closes ctlhandle */
1444 snd_hctl_close(hctl);
1446 if (badHDMI)
1449 * Warn about disconnected devices, but keep them enabled
1450 * Detection can go wrong on Intel, Nvidia and on all
1451 * AMD (fglrx) hardware, so it is not safe to close those
1452 * handles
1454 CLog::Log(LOGDEBUG,
1455 "CAESinkALSA - HDMI device \"{}\" may be unconnected (no ELD data)",
1456 device);
1459 else
1461 snd_ctl_close(ctlhandle);
1466 else if (info.m_deviceType == AE_DEVTYPE_IEC958)
1468 /* append instead of replace, pcminfoName is useful for S/PDIF */
1469 if (!info.m_displayNameExtra.empty())
1470 info.m_displayNameExtra += ' ';
1471 info.m_displayNameExtra += "S/PDIF";
1473 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_AC3);
1474 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_CORE);
1475 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_1024);
1476 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_2048);
1477 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_512);
1478 info.m_dataFormats.push_back(AE_FMT_RAW);
1480 else if (info.m_displayNameExtra.empty())
1482 /* for USB audio, it gets a bit confusing as there is
1483 * - "SB Live! 24-bit External"
1484 * - "SB Live! 24-bit External, S/PDIF"
1485 * so add "Analog" qualifier to the first one */
1486 info.m_displayNameExtra = "Analog";
1489 /* "default" is a device that will be used for all inputs, while
1490 * "@" will be mangled to front/default/surroundXX as necessary */
1491 if (device == "@" || device == "default")
1493 /* Make it "Default (whatever)" */
1494 info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")");
1495 info.m_displayNameExtra = "";
1499 else
1501 /* virtual devices: "default", "pulse", ... */
1502 /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is
1503 * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device",
1504 * so we only use it for virtual devices that have no better display name */
1505 info.m_displayName = description;
1508 snd_pcm_hw_params_t *hwparams;
1509 snd_pcm_hw_params_alloca(&hwparams);
1510 memset(hwparams, 0, snd_pcm_hw_params_sizeof());
1512 /* ensure we can get a playback configuration for the device */
1513 if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
1515 CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"{}\"",
1516 device);
1517 snd_pcm_close(pcmhandle);
1518 return;
1521 /* detect the available sample rates */
1522 for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
1523 if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
1524 info.m_sampleRates.push_back(*rate);
1526 /* detect the channels available */
1527 int channels = 0;
1528 for (int i = ALSA_MAX_CHANNELS; i >= 1; --i)
1530 /* Reopen the device if needed on the special "surroundXX" cases */
1531 if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4))
1532 OpenPCMDevice(device, "", i, &pcmhandle, config);
1534 if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
1536 channels = i;
1537 break;
1541 if (device == "default" && channels == 2)
1543 /* This looks like the ALSA standard default stereo dmix device, we
1544 * probably want to use "@" instead to get surroundXX. */
1545 snd_pcm_close(pcmhandle);
1546 EnumerateDevice(list, "@", description, config);
1547 return;
1550 CAEChannelInfo alsaChannels;
1551 snd_pcm_chmap_query_t** alsaMaps = snd_pcm_query_chmaps(pcmhandle);
1552 bool useEldChannels = (info.m_channels.Count() > 0);
1553 if (alsaMaps)
1555 int i = 0;
1556 for (snd_pcm_chmap_query_t* alsaMap = alsaMaps[i++];
1557 alsaMap; alsaMap = alsaMaps[i++])
1559 CAEChannelInfo AEmap = ALSAchmapToAEChannelMap(&alsaMap->map);
1560 alsaChannels.AddMissingChannels(AEmap);
1561 if (!useEldChannels)
1562 info.m_channels.AddMissingChannels(AEmap);
1564 snd_pcm_free_chmaps(alsaMaps);
1566 else
1568 for (int i = 0; i < channels; ++i)
1570 if (!info.m_channels.HasChannel(LegacyALSAChannelMap[i]))
1571 info.m_channels += LegacyALSAChannelMap[i];
1572 alsaChannels += LegacyALSAChannelMap[i];
1576 /* remove the channels from m_channels that we cant use */
1577 info.m_channels.ResolveChannels(alsaChannels);
1579 /* detect the PCM sample formats that are available */
1580 for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
1582 if (i == AE_FMT_RAW || i == AE_FMT_MAX)
1583 continue;
1584 snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
1585 if (fmt == SND_PCM_FORMAT_UNKNOWN)
1586 continue;
1588 if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
1589 info.m_dataFormats.push_back(i);
1592 if (info.m_deviceType == AE_DEVTYPE_HDMI)
1594 // we don't trust ELD information and push back our supported formats explicitly
1595 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_AC3);
1596 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD);
1597 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_MA);
1598 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_CORE);
1599 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_1024);
1600 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_2048);
1601 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_512);
1602 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_EAC3);
1603 info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_TRUEHD);
1605 // indicate that we can do AE_FMT_RAW
1606 info.m_dataFormats.push_back(AE_FMT_RAW);
1609 snd_pcm_close(pcmhandle);
1610 info.m_wantsIECPassthrough = true;
1611 list.push_back(info);
1614 bool CAESinkALSA::GetELD(snd_hctl_t *hctl, int device, CAEDeviceInfo& info, bool& badHDMI)
1616 badHDMI = false;
1618 snd_ctl_elem_id_t *id;
1619 snd_ctl_elem_info_t *einfo;
1620 snd_ctl_elem_value_t *control;
1621 snd_hctl_elem_t *elem;
1623 snd_ctl_elem_id_alloca(&id);
1624 memset(id, 0, snd_ctl_elem_id_sizeof());
1626 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
1627 snd_ctl_elem_id_set_name (id, "ELD" );
1628 snd_ctl_elem_id_set_device (id, device);
1629 elem = snd_hctl_find_elem(hctl, id);
1630 if (!elem)
1631 return false;
1633 snd_ctl_elem_info_alloca(&einfo);
1634 memset(einfo, 0, snd_ctl_elem_info_sizeof());
1636 if (snd_hctl_elem_info(elem, einfo) < 0)
1637 return false;
1639 if (!snd_ctl_elem_info_is_readable(einfo))
1640 return false;
1642 if (snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_BYTES)
1643 return false;
1645 snd_ctl_elem_value_alloca(&control);
1646 memset(control, 0, snd_ctl_elem_value_sizeof());
1648 if (snd_hctl_elem_read(elem, control) < 0)
1649 return false;
1651 int dataLength = snd_ctl_elem_info_get_count(einfo);
1652 /* if there is no ELD data, then its a bad HDMI device, either nothing attached OR an invalid nVidia HDMI device
1653 * OR the driver doesn't properly support ELD (notably ATI/AMD, 2012-05) */
1654 if (!dataLength)
1655 badHDMI = true;
1656 else
1657 CAEELDParser::Parse(
1658 (const uint8_t*)snd_ctl_elem_value_get_bytes(control),
1659 dataLength,
1660 info
1663 info.m_deviceType = AE_DEVTYPE_HDMI;
1664 return true;
1667 void CAESinkALSA::sndLibErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...)
1669 if (!CServiceBroker::GetLogging().CanLogComponent(LOGAUDIO))
1670 return;
1672 va_list arg;
1673 va_start(arg, fmt);
1674 char *errorStr;
1675 if (vasprintf(&errorStr, fmt, arg) >= 0)
1677 CLog::Log(LOGINFO, "CAESinkALSA - ALSA: {}:{}:({}) {}{}{}", file, line, function, errorStr,
1678 err ? ": " : "", err ? snd_strerror(err) : "");
1679 free(errorStr);
1681 va_end(arg);
1684 void CAESinkALSA::Cleanup()
1686 #if HAVE_LIBUDEV
1687 const auto deviceMonitor = CServiceBroker::GetPlatform().GetService<CALSADeviceMonitor>();
1688 deviceMonitor->Stop();
1689 #endif
1691 #if !defined(HAVE_X11)
1692 const auto controlMonitor = CServiceBroker::GetPlatform().GetService<CALSAHControlMonitor>();
1693 controlMonitor->Clear();
1694 #endif