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.
9 #include "AESinkALSA.h"
11 #include "ServiceBroker.h"
12 #include "cores/AudioEngine/AESinkFactory.h"
13 #include "cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h"
15 #include "cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h"
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"
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 */
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 */
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 */
55 static unsigned int ALSASampleRateList
[] =
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
)
84 snd_config_copy(&config
, original
);
85 return std::unique_ptr
<snd_config_t
, SndConfigDeleter
>(config
, SndConfigDeleter());
89 CAESinkALSA::CAESinkALSA() :
92 /* ensure that ALSA has been initialized */
97 CAESinkALSA::~CAESinkALSA()
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
))
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
:
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
:
146 for (unsigned int i
= 0; i
< count
; ++i
)
147 info
+= ALSAChannelMapPassthrough
[i
];
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
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
])
178 count
= std::max(count
, minChannels
);
179 count
= std::min(count
, maxChannels
);
182 for (unsigned int i
= 0; i
< count
; ++i
)
183 info
+= channelMap
[i
];
188 inline CAEChannelInfo
CAESinkALSA::GetChannelLayout(const AEAudioFormat
& format
, unsigned int channels
)
191 std::string
alsaMapStr("none");
193 if (format
.m_dataFormat
== AE_FMT_RAW
)
195 info
= GetChannelLayoutRaw(format
);
199 /* ask for the actual map */
200 snd_pcm_chmap_t
* actualMap
= snd_pcm_get_chmap(m_pcm
);
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
;
229 info
= GetChannelLayoutLegacy(format
, channels
, channels
);
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
);
244 AEChannel
CAESinkALSA::ALSAChannelToAEChannel(unsigned int 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;
274 unsigned int CAESinkALSA::AEChannelToALSAChannel(AEChannel aeChannel
)
276 unsigned int alsaChannel
;
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;
304 CAEChannelInfo
CAESinkALSA::ALSAchmapToAEChannelMap(snd_pcm_chmap_t
* alsaMap
)
308 for (unsigned int i
= 0; i
< alsaMap
->channels
; i
++)
309 info
+= ALSAChannelToAEChannel(alsaMap
->pos
[i
]);
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
]);
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));
337 std::string
CAESinkALSA::ALSAchmapToString(snd_pcm_chmap_t
* alsaMap
)
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
);
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
))
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
))
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
378 else if (info
.HasChannel(AE_CH_SL
) && !info
.HasChannel(AE_CH_BL
))
381 altLayout
.ReplaceChannel(AE_CH_SL
, AE_CH_BL
);
382 altLayout
.ReplaceChannel(AE_CH_SR
, AE_CH_BR
);
385 else if (!info
.HasChannel(AE_CH_SL
) && info
.HasChannel(AE_CH_BL
))
388 altLayout
.ReplaceChannel(AE_CH_BL
, AE_CH_SL
);
389 altLayout
.ReplaceChannel(AE_CH_BR
, AE_CH_SR
);
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
);
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 */
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
))
425 if (supportedMap
->type
== SND_CHMAP_TYPE_VAR
)
427 /* device supports the AE map directly */
428 chmap
= AEChannelMapToALSAchmap(*selectedInfo
);
433 /* device needs 1:1 remapping */
434 chmap
= CopyALSAchmap(&supportedMap
->map
);
440 /* if no exact chmap was found, fallback to best-effort */
443 CAEChannelInfo allChannels
;
444 std::vector
<CAEChannelInfo
> supportedMapsAE
;
446 /* Convert the ALSA maps to AE maps. */
448 for (snd_pcm_chmap_query_t
* supportedMap
= supportedMaps
[i
++];
449 supportedMap
; supportedMap
= supportedMaps
[i
++])
450 supportedMapsAE
.push_back(ALSAchmapToAEChannelMap(&supportedMap
->map
));
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)
459 int bestAlt
= infoAlternate
.BestMatch(supportedMapsAE
, &scoreAlt
);
460 if (scoreAlt
> score
)
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
);
476 void CAESinkALSA::GetAESParams(const AEAudioFormat
& format
, std::string
& params
)
479 params
= "AES0=0x06";
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;
520 m_passthrough
= false;
523 if (inconfig
.channels
== 0)
525 CLog::Log(LOGERROR
, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
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
);
548 /* get the actual device name that was used */
549 device
= snd_pcm_name(m_pcm
);
552 CLog::Log(LOGINFO
, "CAESinkALSA::Initialize - Opened device \"{}\"", device
);
554 snd_pcm_chmap_t
* selectedChmap
= NULL
;
557 selectedChmap
= SelectALSAChannelMap(format
.m_channelLayout
);
560 /* update wanted channel count according to the selected map */
561 inconfig
.channels
= selectedChmap
->channels
;
565 if (!InitializeHW(inconfig
, outconfig
) || !InitializeSW(outconfig
))
573 /* failure is OK, that likely just means the selected chmap is fixed already */
574 snd_pcm_set_chmap(m_pcm
, 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");
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())
594 format
.m_sampleRate
= outconfig
.sampleRate
;
595 format
.m_frames
= outconfig
.periodSize
;
596 format
.m_frameSize
= outconfig
.frameSize
;
597 format
.m_dataFormat
= outconfig
.format
;
600 m_formatSampleRateMul
= 1.0 / (double)m_format
.m_sampleRate
;
605 snd_pcm_format_t
CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat format
)
607 if (format
== AE_FMT_RAW
)
608 return SND_PCM_FORMAT_S16
;
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
;
620 case AE_FMT_S24NE3
: return SND_PCM_FORMAT_S24_3LE
;
622 case AE_FMT_S32NE
: return SND_PCM_FORMAT_S32
;
623 case AE_FMT_FLOAT
: return SND_PCM_FORMAT_FLOAT
;
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
);
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 */
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
)
685 if (m_passthrough
&& i
!= AE_FMT_S16BE
&& i
!= AE_FMT_S16LE
)
688 fmt
= AEFormatToALSAFormat(i
);
689 if (fmt
== SND_PCM_FORMAT_UNKNOWN
)
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
;
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)
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
));
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");
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 {}",
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");
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
,
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
);
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
);
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");
862 void CAESinkALSA::Deinitialize()
867 snd_pcm_close(m_pcm
);
872 void CAESinkALSA::Stop()
879 void CAESinkALSA::GetDelay(AEDelayStatus
& status
)
886 snd_pcm_sframes_t frames
= 0;
887 snd_pcm_delay(m_pcm
, &frames
);
891 snd_pcm_forward(m_pcm
, -frames
);
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
)
907 CLog::Log(LOGERROR
, "CAESinkALSA - Tried to add packets without a sink");
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)
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
);
926 CLog::Log(LOGERROR
, "CAESinkALSA - snd_pcm_writei({}) {} - trying to recover", ret
,
928 ret
= snd_pcm_recover(m_pcm
, ret
, 1);
931 HandleError("snd_pcm_writei(1)", ret
);
932 ret
= snd_pcm_writei(m_pcm
, buffer
, amount
);
935 HandleError("snd_pcm_writei(2)", ret
);
941 if ( ret
> 0 && snd_pcm_state(m_pcm
) == SND_PCM_STATE_PREPARED
)
942 snd_pcm_start(m_pcm
);
947 frames_written
+= 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
)
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
));
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 */
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
));
980 CLog::Log(LOGERROR
, "CAESinkALSA::HandleError({}) - snd_pcm_writei returned {} ({})", name
,
981 err
, snd_strerror(err
));
986 void CAESinkALSA::Drain()
991 snd_pcm_drain(m_pcm
);
992 snd_pcm_prepare(m_pcm
);
995 void CAESinkALSA::AppendParams(std::string
&device
, const std::string
¶ms
)
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
) ? ':' : ',';
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()) */
1010 if (name
== snd_pcm_name(*pcmp
))
1013 snd_pcm_close(*pcmp
);
1017 int err
= snd_pcm_open_lconf(pcmp
, name
.c_str(), SND_PCM_STREAM_PLAYBACK
, ALSA_OPTIONS
, lconf
);
1020 CLog::Log(LOGINFO
, "CAESinkALSA - Unable to open device \"{}\" for playback", name
);
1026 bool CAESinkALSA::TryDeviceWithParams(const std::string
&name
, const std::string
¶ms
, 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
))
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
¶ms
, 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. */
1058 if (TryDeviceWithParams("surround40" + openName
, params
, pcmp
, lconf
))
1063 if (TryDeviceWithParams("surround51" + openName
, params
, pcmp
, lconf
))
1068 if (TryDeviceWithParams("surround71" + openName
, params
, pcmp
, lconf
))
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
))
1092 /* Try "front" (no dmix, no audio in other channels on subdeviced cards) */
1093 if (TryDeviceWithParams("front" + openName
, params
, pcmp
, lconf
))
1099 /* Non-surroundXX device, just add it */
1100 if (TryDeviceWithParams(name
, params
, pcmp
, lconf
))
1107 void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList
&list
, bool force
)
1109 #if defined(HAVE_LIBUDEV)
1110 const auto deviceMonitor
= CServiceBroker::GetPlatform().GetService
<CALSADeviceMonitor
>();
1111 deviceMonitor
->Start();
1114 /* ensure that ALSA has been initialized */
1115 snd_lib_error_set_handler(sndLibErrorHandler
);
1116 if(!snd_config
|| 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();
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());
1140 if (snd_device_name_hint(-1, "pcm", &hints
) < 0)
1142 CLog::Log(LOGINFO
, "CAESinkALSA - Unable to get a list of devices");
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 */
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
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"
1198 && baseName
!= "dmix"
1199 && baseName
!= "plughw"
1200 && baseName
!= "dsnoop")
1202 EnumerateDevice(list
, name
, desc
? desc
: name
, config
.get());
1209 snd_device_name_free_hint(hints
);
1211 #if !defined(HAVE_X11)
1212 controlMonitor
->Start();
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
);
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
);
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
));
1294 /* if we got here, the configuration is really weird, just append the whole device string */
1296 it2
->m_displayName
+= " (" + it2
->m_deviceName
+ ")";
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
¶m
)
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
);
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
))
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
);
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
);
1376 info
.m_deviceName
= device
;
1377 info
.m_deviceType
= AEDeviceTypeFromName(device
);
1381 /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */
1383 if (snd_card_get_name(cardNr
, &cardName
) == 0)
1385 info
.m_displayName
= 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
);
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)
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");
1437 if (!GetELD(hctl
, dev
, info
, badHDMI
))
1439 "CAESinkALSA - Unable to obtain ELD information for device \"{}\" (not "
1440 "supported by device, or kernel older than 3.2)",
1443 /* snd_hctl_close also closes ctlhandle */
1444 snd_hctl_close(hctl
);
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
1455 "CAESinkALSA - HDMI device \"{}\" may be unconnected (no ELD data)",
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
= "";
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 \"{}\"",
1517 snd_pcm_close(pcmhandle
);
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 */
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)
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
);
1550 CAEChannelInfo alsaChannels
;
1551 snd_pcm_chmap_query_t
** alsaMaps
= snd_pcm_query_chmaps(pcmhandle
);
1552 bool useEldChannels
= (info
.m_channels
.Count() > 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
);
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
)
1584 snd_pcm_format_t fmt
= AEFormatToALSAFormat(i
);
1585 if (fmt
== SND_PCM_FORMAT_UNKNOWN
)
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
)
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
);
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)
1639 if (!snd_ctl_elem_info_is_readable(einfo
))
1642 if (snd_ctl_elem_info_get_type(einfo
) != SND_CTL_ELEM_TYPE_BYTES
)
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)
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) */
1657 CAEELDParser::Parse(
1658 (const uint8_t*)snd_ctl_elem_value_get_bytes(control
),
1663 info
.m_deviceType
= AE_DEVTYPE_HDMI
;
1667 void CAESinkALSA::sndLibErrorHandler(const char *file
, int line
, const char *function
, int err
, const char *fmt
, ...)
1669 if (!CServiceBroker::GetLogging().CanLogComponent(LOGAUDIO
))
1675 if (vasprintf(&errorStr
, fmt
, arg
) >= 0)
1677 CLog::Log(LOGINFO
, "CAESinkALSA - ALSA: {}:{}:({}) {}{}{}", file
, line
, function
, errorStr
,
1678 err
? ": " : "", err
? snd_strerror(err
) : "");
1684 void CAESinkALSA::Cleanup()
1687 const auto deviceMonitor
= CServiceBroker::GetPlatform().GetService
<CALSADeviceMonitor
>();
1688 deviceMonitor
->Stop();
1691 #if !defined(HAVE_X11)
1692 const auto controlMonitor
= CServiceBroker::GetPlatform().GetService
<CALSAHControlMonitor
>();
1693 controlMonitor
->Clear();