jf-roku/source/utils/deviceCapabilities.brs

421 lines
13 KiB
Plaintext
Raw Normal View History

2023-06-02 14:40:30 +00:00
import "pkg:/source/utils/misc.brs"
'Device Capabilities for Roku.
2020-07-11 07:52:52 +00:00
'This will likely need further tweaking
function getDeviceCapabilities() as object
return {
2021-07-09 20:08:32 +00:00
"PlayableMediaTypes": [
"Audio",
"Video"
],
"SupportedCommands": [],
"SupportsPersistentIdentifier": false,
"SupportsMediaControl": false,
"DeviceProfile": getDeviceProfile()
}
end function
' Send Device Profile information to server
sub PostDeviceProfile()
body = getDeviceCapabilities()
req = APIRequest("/Sessions/Capabilities/Full")
req.SetRequest("POST")
postJson(req, FormatJson(body))
end sub
function getDeviceProfile() as object
playMpeg2 = m.global.session.user.settings["playback.mpeg2"]
playAv1 = m.global.session.user.settings["playback.av1"]
'Check if 5.1 Audio Output connected
maxAudioChannels = 2
di = CreateObject("roDeviceInfo")
if di.GetAudioOutputChannel() = "5.1 surround"
2021-07-09 20:08:32 +00:00
maxAudioChannels = 6
end if
2022-07-07 20:13:30 +00:00
addHevcProfile = false
MAIN10 = ""
tsVideoCodecs = "h264"
if di.CanDecodeVideo({ Codec: "hevc" }).Result = true
tsVideoCodecs = "h265,hevc," + tsVideoCodecs
2022-07-07 20:13:30 +00:00
addHevcProfile = true
2022-07-07 19:58:01 +00:00
if di.CanDecodeVideo({ Codec: "hevc", Profile: "main 10" }).Result
MAIN10 = "|main 10"
2022-07-07 19:58:01 +00:00
end if
end if
if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2" }).Result = true
tsVideoCodecs = tsVideoCodecs + ",mpeg2video"
end if
if di.CanDecodeAudio({ Codec: "ac3" }).result
tsAudioCodecs = "aac,ac3"
else
tsAudioCodecs = "aac"
end if
2022-07-07 19:58:01 +00:00
addAv1Profile = false
2022-12-01 08:34:49 +00:00
if playAv1 and di.CanDecodeVideo({ Codec: "av1" }).result
2022-07-07 19:58:01 +00:00
tsVideoCodecs = tsVideoCodecs + ",av1"
addAv1Profile = true
end if
addVp9Profile = false
if di.CanDecodeVideo({ Codec: "vp9" }).result
tsVideoCodecs = tsVideoCodecs + ",vp9"
addVp9Profile = true
end if
2022-07-07 19:58:01 +00:00
hevcVideoRangeTypes = "SDR"
vp9VideoRangeTypes = "SDR"
av1VideoRangeTypes = "SDR"
dp = di.GetDisplayProperties()
if dp.Hdr10 ' or dp.Hdr10Plus?
hevcVideoRangeTypes = hevcVideoRangeTypes + "|HDR10"
vp9VideoRangeTypes = vp9VideoRangeTypes + "|HDR10"
av1VideoRangeTypes = av1VideoRangeTypes + "|HDR10"
2022-07-07 19:58:01 +00:00
end if
if dp.HLG
hevcVideoRangeTypes = hevcVideoRangeTypes + "|HLG"
vp9VideoRangeTypes = vp9VideoRangeTypes + "|HLG"
av1VideoRangeTypes = av1VideoRangeTypes + "|HLG"
2022-07-07 19:58:01 +00:00
end if
if dp.DolbyVision
hevcVideoRangeTypes = hevcVideoRangeTypes + "|DOVI"
2022-07-07 19:58:01 +00:00
'vp9VideoRangeTypes = vp9VideoRangeTypes + ",DOVI" no evidence that vp9 can hold DOVI
av1VideoRangeTypes = av1VideoRangeTypes + "|DOVI"
2022-07-07 19:58:01 +00:00
end if
DirectPlayProfile = GetDirectPlayProfiles()
deviceProfile = {
"MaxStreamingBitrate": 120000000,
"MaxStaticBitrate": 100000000,
"MusicStreamingTranscodingBitrate": 192000,
"DirectPlayProfiles": DirectPlayProfile,
"TranscodingProfiles": [
{
"Container": "aac",
"Type": "Audio",
"AudioCodec": "aac",
"Context": "Streaming",
"Protocol": "http",
2021-07-09 20:08:32 +00:00
"MaxAudioChannels": StrI(maxAudioChannels) ' Currently Jellyfin server expects this as a string
},
{
"Container": "mp3",
"Type": "Audio",
"AudioCodec": "mp3",
"Context": "Streaming",
"Protocol": "http",
"MaxAudioChannels": "2"
},
{
"Container": "mp3",
"Type": "Audio",
"AudioCodec": "mp3",
"Context": "Static",
"Protocol": "http",
"MaxAudioChannels": "2"
},
{
"Container": "aac",
"Type": "Audio",
"AudioCodec": "aac",
"Context": "Static",
"Protocol": "http",
2021-07-09 20:08:32 +00:00
"MaxAudioChannels": StrI(maxAudioChannels) ' Currently Jellyfin server expects this as a string
},
{
"Container": "ts",
"Type": "Video",
"AudioCodec": tsAudioCodecs,
"VideoCodec": tsVideoCodecs,
"Context": "Streaming",
"Protocol": "hls",
2022-05-30 12:59:24 +00:00
"MaxAudioChannels": StrI(maxAudioChannels), ' Currently Jellyfin server expects this as a string
"MinSegments": "1",
"BreakOnNonKeyFrames": true
},
{
"Container": "mp4",
"Type": "Video",
"AudioCodec": "aac,opus,flac,vorbis",
"VideoCodec": "h264",
"Context": "Static",
"Protocol": "http"
}
],
"ContainerProfiles": [],
"CodecProfiles": [
{
"Type": "VideoAudio",
2021-10-12 07:21:31 +00:00
"Codec": DirectPlayProfile[1].AudioCodec, ' Use supported MKV Audio list
"Conditions": [
{
"Condition": "LessThanEqual",
"Property": "AudioChannels",
2022-05-30 12:59:24 +00:00
"Value": StrI(maxAudioChannels), ' Currently Jellyfin server expects this as a string
"IsRequired": false
}
]
},
{
"Type": "Video",
"Codec": "h264",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
"Value": "high|main",
"IsRequired": false
},
{
"Condition": "LessThanEqual",
"Property": "VideoLevel",
"Value": "41",
"IsRequired": false
},
2023-01-25 13:44:26 +00:00
GetBitRateLimit("H264")
]
}
],
"SubtitleProfiles": [
{
"Format": "vtt",
"Method": "External"
},
{
2020-07-11 07:52:52 +00:00
"Format": "srt",
"Method": "External"
},
{
2020-07-11 07:52:52 +00:00
"Format": "ttml",
"Method": "External"
},
{
"Format": "sub",
"Method": "External"
}
2020-07-11 07:52:52 +00:00
]
}
2022-07-07 19:58:01 +00:00
if addAv1Profile
deviceProfile.CodecProfiles.push({
"Type": "Video",
"Codec": "av1",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoRangeType",
"Value": av1VideoRangeTypes,
"IsRequired": false
},
2023-01-25 13:44:26 +00:00
GetBitRateLimit("AV1")
]
})
2022-07-07 20:13:30 +00:00
end if
if addHevcProfile
deviceProfile.CodecProfiles.push({
"Type": "Video",
"Codec": "hevc",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
"Value": "main" + MAIN10,
"IsRequired": false
},
{
"Condition": "EqualsAny",
"Property": "VideoRangeType",
"Value": hevcVideoRangeTypes,
"IsRequired": false
},
{
"Condition": "LessThanEqual",
"Property": "VideoLevel",
"Value": (120 * 5.1).ToStr(),
"IsRequired": false
},
2023-01-25 13:44:26 +00:00
GetBitRateLimit("H265")
]
})
2022-07-07 20:13:30 +00:00
end if
2022-07-07 19:58:01 +00:00
if addVp9Profile
deviceProfile.CodecProfiles.push({
"Type": "Video",
"Codec": "vp9",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoRangeType",
"Value": vp9VideoRangeTypes,
"IsRequired": false
},
2023-01-25 13:44:26 +00:00
GetBitRateLimit("VP9")
]
})
2022-07-07 19:58:01 +00:00
end if
2022-07-07 19:58:01 +00:00
return deviceProfile
2020-07-11 07:52:52 +00:00
end function
function GetDirectPlayProfiles() as object
mp4Video = "h264"
2020-07-11 07:52:52 +00:00
mp4Audio = "mp3,pcm,lpcm,wav"
mkvVideo = "h264,vp8"
mkvAudio = "mp3,pcm,lpcm,wav"
audio = "mp3,pcm,lpcm,wav"
playMpeg2 = m.global.session.user.settings["playback.mpeg2"]
2020-07-11 07:52:52 +00:00
di = CreateObject("roDeviceInfo")
'Check for Supported Video Codecs
2021-07-09 20:08:32 +00:00
if di.CanDecodeVideo({ Codec: "hevc" }).Result = true
2021-08-01 10:45:32 +00:00
mp4Video = mp4Video + ",h265,hevc"
mkvVideo = mkvVideo + ",h265,hevc"
2020-07-11 07:52:52 +00:00
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeVideo({ Codec: "vp9" }).Result = true
mkvVideo = mkvVideo + ",vp9"
2020-07-11 07:52:52 +00:00
end if
if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2" }).Result = true
mp4Video = mp4Video + ",mpeg2video"
mkvVideo = mkvVideo + ",mpeg2video"
end if
if m.global.session.user.settings["playback.mpeg4"] = true
mp4Video = mp4Video + ",mpeg4"
end if
2020-07-11 07:52:52 +00:00
' Check for supported Audio
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "ac3" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",ac3"
mp4Audio = mp4Audio + ",ac3"
audio = audio + ",ac3"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "wma" }).result
2020-07-11 07:52:52 +00:00
audio = audio + ",wma"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "flac" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",flac"
audio = audio + ",flac"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "alac" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",alac"
mp4Audio = mp4Audio + ",alac"
audio = audio + ",alac"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "aac" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",aac"
mp4Audio = mp4Audio + ",aac"
audio = audio + ",aac"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "opus" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",opus"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "dts" }).result
mkvAudio = mkvAudio + ",dts"
audio = audio + ",dts"
2020-07-11 07:52:52 +00:00
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "wmapro" }).result
2020-07-11 07:52:52 +00:00
audio = audio + ",wmapro"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "vorbis" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",vorbis"
end if
2021-07-09 20:08:32 +00:00
if di.CanDecodeAudio({ Codec: "eac3" }).result
2020-07-11 07:52:52 +00:00
mkvAudio = mkvAudio + ",eac3"
mp4Audio = mp4Audio + ",eac3"
audio = audio + ",eac3"
2020-07-11 07:52:52 +00:00
end if
return [
2021-07-09 20:08:32 +00:00
{
"Container": "mp4,m4v,mov",
"Type": "Video",
"VideoCodec": mp4Video,
"AudioCodec": mp4Audio
},
{
"Container": "mkv,webm",
"Type": "Video",
"VideoCodec": mkvVideo,
"AudioCodec": mkvAudio
},
{
"Container": audio,
2022-05-30 13:00:14 +00:00
"Type": "Audio"
2021-07-09 20:08:32 +00:00
}
2020-07-11 07:52:52 +00:00
]
end function
2023-01-25 13:44:26 +00:00
function GetBitRateLimit(codec as string)
if m.global.session.user.settings["playback.bitrate.maxlimited"] = true
userSetLimit = m.global.session.user.settings["playback.bitrate.limit"]
2023-06-02 14:40:30 +00:00
if isValid(userSetLimit) and type(userSetLimit) = "Integer" and userSetLimit > 0
userSetLimit *= 1000000
return {
"Condition": "LessThanEqual",
"Property": "VideoBitrate",
2023-02-04 13:54:39 +00:00
"Value": userSetLimit.ToStr(),
IsRequired: true
}
2023-02-04 13:54:39 +00:00
else
' Some repeated values (e.g. same "40mbps" for several codecs)
' but this makes it easy to update in the future if the bitrates start to deviate.
if codec = "H264"
' Roku only supports h264 up to 10Mpbs
return {
"Condition": "LessThanEqual",
"Property": "VideoBitrate",
"Value": "10000000",
IsRequired: true
}
else if codec = "AV1"
' Roku only supports AV1 up to 40Mpbs
return {
"Condition": "LessThanEqual",
"Property": "VideoBitrate",
"Value": "40000000",
IsRequired: true
}
else if codec = "H265"
' Roku only supports h265 up to 40Mpbs
return {
"Condition": "LessThanEqual",
"Property": "VideoBitrate",
"Value": "40000000",
IsRequired: true
}
else if codec = "VP9"
' Roku only supports VP9 up to 40Mpbs
return {
"Condition": "LessThanEqual",
"Property": "VideoBitrate",
"Value": "40000000",
IsRequired: true
}
end if
end if
2023-01-25 13:44:26 +00:00
end if
return {}
end function