Skip to content

Commit

Permalink
CAESinkPipewire: add passthrough support
Browse files Browse the repository at this point in the history
This requires adding iec958Codecs to your output node.

For example:
pw-cli s 44 Props '{ iec958Codecs : [ PCM DTS AC3 EAC3 TrueHD DTS-HD] }'

where 44 is your output node id

or

cat > ~/.config/wireplumber/main.lua.d/51-alsa.lua << EOF
rule = {
  matches = {
    {
      { "node.name", "matches", "alsa_output.*" },
    },
  },
  apply_properties = {
    ["iec958.codecs"] = "[ PCM DTS AC3 EAC3 TrueHD DTS-HD ]",
  },
}

table.insert(alsa_monitor.rules, rule)
EOF

Signed-off-by: Lukas Rusak <lorusak@gmail.com>
  • Loading branch information
lrusak committed Jan 26, 2023
1 parent 1067e72 commit f6754be
Showing 1 changed file with 108 additions and 12 deletions.
120 changes: 108 additions & 12 deletions xbmc/cores/AudioEngine/Sinks/pipewire/AESinkPipewire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ constexpr std::array<uint32_t, 14> defaultSampleRates = {
// clang-format on

constexpr auto formatMap = make_map<spa_audio_format, AEDataFormat>(
{{SPA_AUDIO_FORMAT_U8, AEDataFormat::AE_FMT_U8},
{{SPA_AUDIO_FORMAT_ENCODED, AEDataFormat::AE_FMT_RAW},
{SPA_AUDIO_FORMAT_U8, AEDataFormat::AE_FMT_U8},
{SPA_AUDIO_FORMAT_S16, AEDataFormat::AE_FMT_S16NE},
{SPA_AUDIO_FORMAT_S24_32, AEDataFormat::AE_FMT_S24NE4},
{SPA_AUDIO_FORMAT_S32, AEDataFormat::AE_FMT_S32NE},
Expand All @@ -63,6 +64,7 @@ constexpr uint8_t PWFormatToSampleSize(spa_audio_format format)
case SPA_AUDIO_FORMAT_S8:
case SPA_AUDIO_FORMAT_U8:
return 1;
case SPA_AUDIO_FORMAT_ENCODED:
case SPA_AUDIO_FORMAT_S16:
return 2;
case SPA_AUDIO_FORMAT_S24:
Expand All @@ -80,6 +82,8 @@ constexpr std::string_view PWFormatToString(spa_audio_format format)
{
switch (format)
{
case SPA_AUDIO_FORMAT_ENCODED:
return "encoded";
case SPA_AUDIO_FORMAT_U8:
return "u8";
case SPA_AUDIO_FORMAT_S8:
Expand Down Expand Up @@ -165,6 +169,57 @@ CAEChannelInfo PWChannelMapToAEChannelMap(std::vector<spa_audio_channel>& channe
return channels;
}

constexpr auto iec958CodecMap = make_map<CAEStreamInfo::DataType, spa_audio_iec958_codec>({
{CAEStreamInfo::STREAM_TYPE_DTS_512, SPA_AUDIO_IEC958_CODEC_DTS},
{CAEStreamInfo::STREAM_TYPE_DTS_1024, SPA_AUDIO_IEC958_CODEC_DTS},
{CAEStreamInfo::STREAM_TYPE_DTS_2048, SPA_AUDIO_IEC958_CODEC_DTS},
{CAEStreamInfo::STREAM_TYPE_DTSHD_CORE, SPA_AUDIO_IEC958_CODEC_DTS},

{CAEStreamInfo::STREAM_TYPE_AC3, SPA_AUDIO_IEC958_CODEC_AC3},

{CAEStreamInfo::STREAM_TYPE_EAC3, SPA_AUDIO_IEC958_CODEC_EAC3},

{CAEStreamInfo::STREAM_TYPE_TRUEHD, SPA_AUDIO_IEC958_CODEC_TRUEHD},

{CAEStreamInfo::STREAM_TYPE_DTSHD, SPA_AUDIO_IEC958_CODEC_DTSHD},
{CAEStreamInfo::STREAM_TYPE_DTSHD_MA, SPA_AUDIO_IEC958_CODEC_DTSHD},
});

constexpr spa_audio_iec958_codec AEStreamInfoDataTypeToPWIEC958Codec(
const CAEStreamInfo::DataType& type)
{
const auto it = iec958CodecMap.find(type);
if (it != iec958CodecMap.cend())
return it->second;

return SPA_AUDIO_IEC958_CODEC_UNKNOWN;
}

// constexpr in c++20
std::vector<CAEStreamInfo::DataType> PWIEC958CodecToAEStreamInfoDataTypeList(
const spa_audio_iec958_codec& codec)
{
// clang-format off
auto codecMap = std::map<spa_audio_iec958_codec, std::vector<CAEStreamInfo::DataType>>({
{SPA_AUDIO_IEC958_CODEC_DTS, {CAEStreamInfo::STREAM_TYPE_DTS_512,
CAEStreamInfo::STREAM_TYPE_DTS_1024,
CAEStreamInfo::STREAM_TYPE_DTS_2048,
CAEStreamInfo::STREAM_TYPE_DTSHD_CORE}},
{SPA_AUDIO_IEC958_CODEC_AC3, {CAEStreamInfo::STREAM_TYPE_AC3}},
{SPA_AUDIO_IEC958_CODEC_EAC3, {CAEStreamInfo::STREAM_TYPE_EAC3}},
{SPA_AUDIO_IEC958_CODEC_TRUEHD, {CAEStreamInfo::STREAM_TYPE_TRUEHD}},
{SPA_AUDIO_IEC958_CODEC_DTSHD, {CAEStreamInfo::STREAM_TYPE_DTSHD,
CAEStreamInfo::STREAM_TYPE_DTSHD_MA}},
});
// clang-format on

const auto it = codecMap.find(codec);
if (it != codecMap.cend())
return it->second;

return {};
}

std::chrono::duration<double, std::ratio<1>> PWTimeToAEDelay(const pw_time& time,
const uint32_t& samplerate)
{
Expand Down Expand Up @@ -307,6 +362,19 @@ void CAESinkPipewire::EnumerateDevicesEx(AEDeviceInfoList& list, bool force)
device.m_channels += ch->second;
}

for (const auto& iec958Codec : node->GetIEC958Codecs())
{
auto streamTypes = PWIEC958CodecToAEStreamInfoDataTypeList(iec958Codec);
device.m_streamTypes.insert(device.m_streamTypes.end(), streamTypes.begin(),
streamTypes.end());
}

if (device.m_channels.Count() == 2 && !device.m_streamTypes.empty())
{
device.m_deviceType = AE_DEVTYPE_IEC958;
device.m_dataFormats.emplace_back(AE_FMT_RAW);
}

list.emplace_back(device);
}

Expand Down Expand Up @@ -346,6 +414,17 @@ bool CAESinkPipewire::Initialize(AEAudioFormat& format, std::string& device)
id = target->first;
}

bool passthrough = (format.m_dataFormat == AE_FMT_RAW);

if (passthrough)
{
format.m_channelLayout = AE_CH_LAYOUT_2_0;

if (format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_DTSHD_MA ||
format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD)
format.m_channelLayout = AE_CH_LAYOUT_7_1;
}

m_stream = std::make_unique<PIPEWIRE::CPipewireStream>(core);

m_latency = DEFAULT_BUFFER_DURATION;
Expand Down Expand Up @@ -380,22 +459,11 @@ bool CAESinkPipewire::Initialize(AEAudioFormat& format, std::string& device)
format.m_sampleRate,
static_cast<double>(frames) / DEFAULT_LATENCY_DIVIDER / format.m_sampleRate);

spa_audio_info_raw info{};
info.format = pwFormat;
info.flags = SPA_AUDIO_FLAG_NONE;
info.rate = format.m_sampleRate;
info.channels = static_cast<uint32_t>(pwChannels.size());

for (size_t index = 0; index < pwChannels.size(); index++)
info.position[index] = pwChannels[index];

std::array<uint8_t, 1024> buffer;
auto builder = SPA_POD_BUILDER_INIT(buffer.data(), buffer.size());

std::vector<const spa_pod*> params;

params.emplace_back(spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &info));

// clang-format off
params.emplace_back(static_cast<const spa_pod*>(spa_pod_builder_add_object(
&builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
Expand All @@ -409,6 +477,34 @@ bool CAESinkPipewire::Initialize(AEAudioFormat& format, std::string& device)
static_cast<pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE |
PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_DRIVER);

if (!passthrough)
{
spa_audio_info_raw info{};
info.format = pwFormat;
info.flags = SPA_AUDIO_FLAG_NONE;
info.rate = format.m_sampleRate;
info.channels = static_cast<uint32_t>(pwChannels.size());

for (size_t index = 0; index < pwChannels.size(); index++)
info.position[index] = pwChannels[index];

params.emplace_back(spa_format_audio_raw_build(&builder, SPA_PARAM_EnumFormat, &info));
}

if (passthrough)
{
spa_audio_info_iec958 info{};
info.codec = AEStreamInfoDataTypeToPWIEC958Codec(format.m_streamInfo.m_type);
info.flags = SPA_AUDIO_FLAG_NONE;
info.rate = format.m_sampleRate;

params.emplace_back(spa_format_audio_iec958_build(&builder, SPA_PARAM_EnumFormat, &info));

flags = static_cast<pw_stream_flags>(PW_STREAM_FLAG_EXCLUSIVE | static_cast<int>(flags));

format.m_dataFormat = AE_FMT_S16NE;
}

if (!m_stream->Connect(id, PW_DIRECTION_OUTPUT, params, flags))
{
loop.Unlock();
Expand Down

0 comments on commit f6754be

Please sign in to comment.