From 297ad0b0c5b5f47deea0e41093123a9878aff966 Mon Sep 17 00:00:00 2001
From: MichaelRUSF <78215956+MichaelRUSF@users.noreply.github.com>
Date: Mon, 26 Aug 2024 12:58:57 -0400
Subject: [PATCH] Add SPDIF output mode
---
.../preference/constant/AudioBehavior.kt | 5 ++
.../ui/playback/PlaybackController.java | 3 +-
.../util/profile/ExoPlayerProfile.kt | 63 +++++++++++++++----
.../androidtv/util/profile/ProfileHelper.kt | 14 +++++
app/src/main/res/values/strings.xml | 1 +
5 files changed, 72 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/constant/AudioBehavior.kt b/app/src/main/java/org/jellyfin/androidtv/preference/constant/AudioBehavior.kt
index 21dfbfadc4..960543d08e 100644
--- a/app/src/main/java/org/jellyfin/androidtv/preference/constant/AudioBehavior.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/preference/constant/AudioBehavior.kt
@@ -15,4 +15,9 @@ enum class AudioBehavior(
* Downnmix audio to stereo. Disables the AC3, EAC3 and AAC_LATM audio codecs.
*/
DOWNMIX_TO_STEREO(R.string.pref_audio_compat),
+
+ /**
+ * Allow only the AC3, EAC3 and DTS codecs. All others capped to stereo output
+ */
+ SPDIF_OUTPUT(R.string.audio_spdif)
}
diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
index e140a12f7d..5bc654c1fc 100644
--- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
+++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
@@ -524,7 +524,8 @@ private VideoOptions buildExoPlayerOptions(@Nullable Integer forcedSubtitleIndex
isLiveTv && !userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()),
userPreferences.getValue().get(UserPreferences.Companion.getAc3Enabled()),
userPreferences.getValue().get(UserPreferences.Companion.getAudioBehaviour()) == AudioBehavior.DOWNMIX_TO_STEREO,
- !DeviceUtils.has4kVideoSupport()
+ !DeviceUtils.has4kVideoSupport(),
+ userPreferences.getValue().get(UserPreferences.Companion.getAudioBehaviour()) == AudioBehavior.SPDIF_OUTPUT
);
internalOptions.setProfile(internalProfile);
return internalOptions;
diff --git a/app/src/main/java/org/jellyfin/androidtv/util/profile/ExoPlayerProfile.kt b/app/src/main/java/org/jellyfin/androidtv/util/profile/ExoPlayerProfile.kt
index 0ab9ca98dc..2e3ccfb140 100644
--- a/app/src/main/java/org/jellyfin/androidtv/util/profile/ExoPlayerProfile.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/util/profile/ExoPlayerProfile.kt
@@ -9,6 +9,7 @@ import org.jellyfin.androidtv.util.profile.ProfileHelper.deviceHevcCodecProfile
import org.jellyfin.androidtv.util.profile.ProfileHelper.deviceHevcLevelCodecProfiles
import org.jellyfin.androidtv.util.profile.ProfileHelper.max1080pProfileConditions
import org.jellyfin.androidtv.util.profile.ProfileHelper.maxAudioChannelsCodecProfile
+import org.jellyfin.androidtv.util.profile.ProfileHelper.maxSPDIFAudioChannelProfile
import org.jellyfin.androidtv.util.profile.ProfileHelper.photoDirectPlayProfile
import org.jellyfin.androidtv.util.profile.ProfileHelper.subtitleProfile
import org.jellyfin.androidtv.util.profile.ProfileHelper.supportsHevc
@@ -28,7 +29,8 @@ class ExoPlayerProfile(
disableVideoDirectPlay: Boolean,
isAC3Enabled: Boolean,
downMixAudio: Boolean,
- disable4KVideo: Boolean
+ disable4KVideo: Boolean,
+ enableSPDIF: Boolean
) : DeviceProfile() {
private val downmixSupportedAudioCodecs = arrayOf(
Codec.Audio.AAC,
@@ -36,20 +38,31 @@ class ExoPlayerProfile(
Codec.Audio.MP2
)
+ private val downmixSPDIFSupportedAudioCodecs = arrayOf(
+ // Bypass isAC3Enabled check for SPDIF
+ Codec.Audio.AC3,
+ Codec.Audio.MP3,
+ Codec.Audio.MP2
+ )
+
/**
* Returns all audio codecs used commonly in video containers.
* This does not include containers / codecs found in audio files
*/
private val allSupportedAudioCodecs = buildList {
addAll(downmixSupportedAudioCodecs)
+ if ((isAC3Enabled) || (enableSPDIF)) {
+ add(Codec.Audio.AC3)
+ add(Codec.Audio.EAC3)
+ }
+ if (!enableSPDIF) {
+ add(Codec.Audio.MLP)
+ add(Codec.Audio.TRUEHD)
+ }
add(Codec.Audio.AAC_LATM)
add(Codec.Audio.ALAC)
- if (isAC3Enabled) add(Codec.Audio.AC3)
- if (isAC3Enabled) add(Codec.Audio.EAC3)
add(Codec.Audio.DCA)
add(Codec.Audio.DTS)
- add(Codec.Audio.MLP)
- add(Codec.Audio.TRUEHD)
add(Codec.Audio.PCM_ALAW)
add(Codec.Audio.PCM_MULAW)
add(Codec.Audio.PCM_S16LE)
@@ -70,6 +83,18 @@ class ExoPlayerProfile(
maxStreamingBitrate = 20_000_000 // 20 mbps
maxStaticBitrate = 10_000_0000 // 10 mbps
+ // Determine audio codecs for profiles
+ val audioCodecForTranscodingProfile = when {
+ downMixAudio -> downmixSupportedAudioCodecs
+ enableSPDIF -> downmixSPDIFSupportedAudioCodecs
+ else -> allSupportedAudioCodecsWithoutFFmpegExperimental
+ }.joinToString(",")
+
+ val audioCodecForDirectPlayProfile = when {
+ downMixAudio -> downmixSupportedAudioCodecs
+ else -> allSupportedAudioCodecsWithoutFFmpegExperimental
+ }.joinToString(",")
+
transcodingProfiles = arrayOf(
// TS video profile
TranscodingProfile().apply {
@@ -80,10 +105,7 @@ class ExoPlayerProfile(
if (supportsHevc) add(Codec.Video.HEVC)
add(Codec.Video.H264)
}.joinToString(",")
- audioCodec = when (downMixAudio) {
- true -> downmixSupportedAudioCodecs
- false -> allSupportedAudioCodecsWithoutFFmpegExperimental
- }.joinToString(",")
+ audioCodec = audioCodecForTranscodingProfile
protocol = "hls"
copyTimestamps = false
},
@@ -128,10 +150,7 @@ class ExoPlayerProfile(
Codec.Video.AV1
).joinToString(",")
- audioCodec = when (downMixAudio) {
- true -> downmixSupportedAudioCodecs
- false -> allSupportedAudioCodecs
- }.joinToString(",")
+ audioCodec = audioCodecForDirectPlayProfile
})
}
// Audio direct play
@@ -208,6 +227,24 @@ class ExoPlayerProfile(
}
// Audio channel profile
add(maxAudioChannelsCodecProfile(channels = if (downMixAudio) 2 else 8))
+ // Add maximum audio channel profiles for SPDIF if enableSPDIF is true
+ if (enableSPDIF) {
+ arrayOf(
+ Codec.Audio.AAC,
+ Codec.Audio.AAC_LATM,
+ Codec.Audio.ALAC,
+ Codec.Audio.PCM_ALAW,
+ Codec.Audio.PCM_MULAW,
+ Codec.Audio.PCM_S16LE,
+ Codec.Audio.PCM_S20LE,
+ Codec.Audio.PCM_S24LE,
+ Codec.Audio.OPUS,
+ Codec.Audio.FLAC,
+ Codec.Audio.VORBIS
+ ).forEach { codec ->
+ add(maxSPDIFAudioChannelProfile(codec, 2))
+ }
+ }
}.toTypedArray()
subtitleProfiles = arrayOf(
diff --git a/app/src/main/java/org/jellyfin/androidtv/util/profile/ProfileHelper.kt b/app/src/main/java/org/jellyfin/androidtv/util/profile/ProfileHelper.kt
index 87e163a23e..53ed3b2117 100644
--- a/app/src/main/java/org/jellyfin/androidtv/util/profile/ProfileHelper.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/util/profile/ProfileHelper.kt
@@ -297,6 +297,20 @@ object ProfileHelper {
)
}
+ fun maxSPDIFAudioChannelProfile(codec: String, maxChannels: Int): CodecProfile {
+ return CodecProfile().apply {
+ type = CodecType.VideoAudio
+ this.codec = codec
+ conditions = arrayOf(
+ ProfileCondition(
+ ProfileConditionType.LessThanEqual,
+ ProfileConditionValue.AudioChannels,
+ maxChannels.toString()
+ )
+ )
+ }
+ }
+
internal fun subtitleProfile(
format: String,
method: SubtitleDeliveryMethod
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f8a51dce72..3622c95440 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -516,6 +516,7 @@
Past 24 hours
Prefer FFmpeg for audio playback
Use FFmpeg to decode audio, even if platform codecs are available.
+ SPDIF
- %1$s second
- %1$s seconds