0.2.42 - DTS surround sound support

This commit is contained in:
ponzischeme89
2026-08-10 12:23:23 +12:00
parent 32c0054c44
commit f41db66ac6
29 changed files with 1363 additions and 128 deletions
+104 -18
View File
@@ -3,6 +3,7 @@
//
// Wholphin: https://github.com/damontecres/Wholphin
// Jellyfin Android TV: https://github.com/jellyfin/jellyfin-androidtv
// Moonfin audio profile: https://github.com/Moonfin-Client/Moonfin-Core
//
// Modifications Copyright (C) 2026 Memby contributors
// SPDX-License-Identifier: GPL-2.0-only
@@ -10,49 +11,134 @@ package emby
import "strconv"
var alwaysDecodableAudioCodecs = []string{
"aac", "mp3", "flac", "opus", "vorbis", "pcm_s16le", "pcm_s24le",
}
// PlaybackCapabilities is the Android decoder evidence captured for one TV session.
// Zero values deliberately describe the legacy H.264-safe profile.
type PlaybackCapabilities struct {
H264Profiles []string
H264Level int
H264High10Level int
H264MaxWidth int
H264MaxHeight int
HEVC bool
HEVCMain bool
HEVCMain10 bool
HEVCMainLevel int
HEVCMain10Level int
HEVCMaxWidth int
HEVCMaxHeight int
HEVCHDR10 bool
HEVCHDR10Plus bool
HEVCDolbyVision bool
H264Profiles []string
H264Level int
H264High10Level int
H264MaxWidth int
H264MaxHeight int
HEVC bool
HEVCMain bool
HEVCMain10 bool
HEVCMainLevel int
HEVCMain10Level int
HEVCMaxWidth int
HEVCMaxHeight int
HEVCHDR10 bool
HEVCHDR10Plus bool
HEVCDolbyVision bool
AudioProfileV1 bool
AudioPassthrough map[string]bool
AudioDecode map[string]bool
AudioMaxChannels int
}
func androidTVDeviceProfile(capabilities PlaybackCapabilities) map[string]any {
videoCodecs := directPlayVideoCodecs(capabilities.HEVC)
audioCodecs := directPlayAudioCodecs(capabilities)
transcodeAudio := transcodeAudioCodecs(capabilities)
return map[string]any{
"Name": "Memby Android TV", "SupportedMediaTypes": "Video",
"DirectPlayProfiles": []map[string]string{
{
"Container": "mkv,mp4,m4v,mov,ts,mpegts", "VideoCodec": videoCodecs,
"AudioCodec": "aac,mp3", "Type": "Video",
"AudioCodec": audioCodecs, "Type": "Video",
},
},
"TranscodingProfiles": []map[string]string{
{
// AllowVideoStreamCopy lets Emby keep a supported H.264/HEVC track and
// convert only incompatible audio or subtitles into an HLS stream.
"Container": "ts", "VideoCodec": videoCodecs, "AudioCodec": "aac",
"Container": "ts", "VideoCodec": videoCodecs, "AudioCodec": transcodeAudio,
"Protocol": "hls", "Type": "Video", "Context": "Streaming",
},
},
"CodecProfiles": videoCodecProfiles(capabilities),
"CodecProfiles": append(videoCodecProfiles(capabilities), audioCodecProfile(capabilities)),
"SubtitleProfiles": androidTVSubtitleProfiles(),
}
}
func directPlayAudioCodecs(capabilities PlaybackCapabilities) string {
if !capabilities.AudioProfileV1 {
return "aac,mp3"
}
codecs := append([]string{}, alwaysDecodableAudioCodecs...)
for _, codec := range []string{"ac3", "eac3", "atmos", "dts", "dts_hd", "truehd"} {
if capabilities.AudioPassthrough[codec] || capabilities.AudioDecode[codec] {
switch codec {
case "dts":
codecs = appendUnique(codecs, "dts", "dca")
case "dts_hd":
codecs = appendUnique(codecs, "dts", "dca", "dtshd")
case "truehd":
codecs = appendUnique(codecs, "truehd", "mlp")
case "atmos":
codecs = appendUnique(codecs, "eac3")
default:
codecs = appendUnique(codecs, codec)
}
}
}
return joinComma(codecs)
}
func transcodeAudioCodecs(capabilities PlaybackCapabilities) string {
codecs := []string{}
if capabilities.AudioPassthrough["eac3"] || capabilities.AudioPassthrough["atmos"] {
codecs = append(codecs, "eac3")
}
if capabilities.AudioPassthrough["ac3"] {
codecs = append(codecs, "ac3")
}
return joinComma(appendUnique(codecs, "aac", "mp3"))
}
func audioCodecProfile(capabilities PlaybackCapabilities) map[string]any {
channels := capabilities.AudioMaxChannels
if channels < 2 {
channels = 2
}
return map[string]any{
"Type": "VideoAudio", "Codec": "",
"Conditions": []map[string]any{
profileCondition("LessThanEqual", "AudioChannels", strconv.Itoa(channels)),
},
}
}
func appendUnique(values []string, additions ...string) []string {
for _, addition := range additions {
found := false
for _, value := range values {
if value == addition {
found = true
break
}
}
if !found {
values = append(values, addition)
}
}
return values
}
func joinComma(values []string) string {
result := ""
for _, value := range values {
if result != "" {
result += ","
}
result += value
}
return result
}
// forceH264TranscodeProfile turns decoder recovery into an actual codec change. Merely
// removing DirectPlayProfiles is insufficient: Emby may otherwise stream-copy HEVC into
// the ordinary HLS transcoding profile and hand the failing decoder the same video again.