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