From ba31ab7372170e9b47695b67f5441bc936280438 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 31 May 2023 23:19:03 -0400 Subject: [PATCH 01/60] update hevc and mpeg2 CodecProfiles + always include container when checking a codec for ts container --- source/utils/deviceCapabilities.brs | 161 ++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 31 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index e42a8c54..4872b5cb 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -5,10 +5,11 @@ function getDeviceCapabilities() as object return { "PlayableMediaTypes": [ "Audio", - "Video" + "Video", + "Photo" ], "SupportedCommands": [], - "SupportsPersistentIdentifier": false, + "SupportsPersistentIdentifier": true, "SupportsMediaControl": false, "DeviceProfile": getDeviceProfile() } @@ -16,52 +17,136 @@ end function ' Send Device Profile information to server sub PostDeviceProfile() - body = getDeviceCapabilities() + profile = getDeviceCapabilities() req = APIRequest("/Sessions/Capabilities/Full") req.SetRequest("POST") - postJson(req, FormatJson(body)) + print "profile =", profile + print "profile.DeviceProfile =", profile.DeviceProfile + print "profile.DeviceProfile.CodecProfiles =" + for each prof in profile.DeviceProfile.CodecProfiles + print prof + for each cond in prof.Conditions + print cond + end for + end for + print "profile.DeviceProfile.ContainerProfiles =", profile.DeviceProfile.ContainerProfiles + print "profile.DeviceProfile.DirectPlayProfiles =" + for each prof in profile.DeviceProfile.DirectPlayProfiles + print prof + end for + print "profile.DeviceProfile.SubtitleProfiles =" + for each prof in profile.DeviceProfile.SubtitleProfiles + print prof + end for + print "profile.DeviceProfile.TranscodingProfiles =" + for each prof in profile.DeviceProfile.TranscodingProfiles + print prof + end for + print "profile.PlayableMediaTypes =", profile.PlayableMediaTypes + print "profile.SupportedCommands =", profile.SupportedCommands + postJson(req, FormatJson(profile)) end sub function getDeviceProfile() as object playMpeg2 = get_user_setting("playback.mpeg2") = "true" playAv1 = get_user_setting("playback.av1") = "true" + di = CreateObject("roDeviceInfo") + + maxAudioChannels = "2" ' Currently Jellyfin server expects this as a string + tsVideoCodecs = "h264" + tsAudioCodecs = "aac" 'Check if 5.1 Audio Output connected - maxAudioChannels = 2 - di = CreateObject("roDeviceInfo") if di.GetAudioOutputChannel() = "5.1 surround" - maxAudioChannels = 6 + maxAudioChannels = "6" end if + ' HEVC addHevcProfile = false - MAIN10 = "" - tsVideoCodecs = "h264" - if di.CanDecodeVideo({ Codec: "hevc" }).Result = true + hevcProfileString = "" + hevcHighestLevel = 4.1 + + if di.CanDecodeVideo({ Codec: "hevc", Container: "ts" }).Result = true tsVideoCodecs = "h265,hevc," + tsVideoCodecs addHevcProfile = true - if di.CanDecodeVideo({ Codec: "hevc", Profile: "main 10" }).Result - MAIN10 = "|main 10" - end if + + hevcProfiles = ["main", "main 10"] + hevcLevels = ["4.1", "5.0", "5.1"] + supportArray = {} + + for each profile in hevcProfiles + for each level in hevcLevels + if di.CanDecodeVideo({ Codec: "hevc", Container: "ts", Profile: profile, Level: level }).Result + if supportArray[profile] = invalid + supportArray[profile] = [] + if hevcProfileString = "" + hevcProfileString = profile + else + hevcProfileString = hevcProfileString + "|" + profile + end if + end if + + supportArray[profile].Push(level) + end if + end for + end for + + for each prof in supportArray + highestLevelString = supportArray[prof].Pop() + if highestLevelString = "5" + hevcHighestLevel = 5 + end if + if highestLevelString = "5.1" + hevcHighestLevel = 5.1 + end if + end for end if - if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2" }).Result = true + ' MPEG2 + addMpeg2Profile = false + mpeg2LevelString = "" + if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2", Container: "ts" }).Result = true tsVideoCodecs = tsVideoCodecs + ",mpeg2video" + addMpeg2Profile = true + + mpeg2Levels = ["main", "high"] + + for each level in mpeg2Levels + if di.CanDecodeVideo({ Codec: "mpeg2", Container: "ts", Level: level }).Result + if mpeg2LevelString = "" + mpeg2LevelString = level + else + mpeg2LevelString = mpeg2LevelString + "|" + level + end if + end if + end for end if - if di.CanDecodeAudio({ Codec: "ac3" }).result - tsAudioCodecs = "aac,ac3" - else - tsAudioCodecs = "aac" + if di.CanDecodeAudio({ Codec: "mp3", Container: "ts" }).result + tsAudioCodecs = tsAudioCodecs + ",mp3" + end if + + if di.CanDecodeAudio({ Codec: "dts", Container: "ts" }).result + tsAudioCodecs = "dts," + tsAudioCodecs + end if + + if di.CanDecodeAudio({ Codec: "ac3", Container: "ts" }).result + tsAudioCodecs = "ac3," + tsAudioCodecs + end if + + ' prefer eac3 over all other audio codecs + if di.CanDecodeAudio({ Codec: "eac3", Container: "ts" }).result + tsAudioCodecs = "eac3," + tsAudioCodecs end if addAv1Profile = false - if playAv1 and di.CanDecodeVideo({ Codec: "av1" }).result + if playAv1 and di.CanDecodeVideo({ Codec: "av1", Container: "ts" }).result tsVideoCodecs = tsVideoCodecs + ",av1" addAv1Profile = true end if addVp9Profile = false - if di.CanDecodeVideo({ Codec: "vp9" }).result + if di.CanDecodeVideo({ Codec: "vp9", Container: "ts" }).result tsVideoCodecs = tsVideoCodecs + ",vp9" addVp9Profile = true end if @@ -101,7 +186,7 @@ function getDeviceProfile() as object "AudioCodec": "aac", "Context": "Streaming", "Protocol": "http", - "MaxAudioChannels": StrI(maxAudioChannels) ' Currently Jellyfin server expects this as a string + "MaxAudioChannels": maxAudioChannels }, { "Container": "mp3", @@ -125,7 +210,7 @@ function getDeviceProfile() as object "AudioCodec": "aac", "Context": "Static", "Protocol": "http", - "MaxAudioChannels": StrI(maxAudioChannels) ' Currently Jellyfin server expects this as a string + "MaxAudioChannels": maxAudioChannels }, { "Container": "ts", @@ -134,7 +219,7 @@ function getDeviceProfile() as object "VideoCodec": tsVideoCodecs, "Context": "Streaming", "Protocol": "hls", - "MaxAudioChannels": StrI(maxAudioChannels), ' Currently Jellyfin server expects this as a string + "MaxAudioChannels": maxAudioChannels, "MinSegments": "1", "BreakOnNonKeyFrames": true }, @@ -156,7 +241,7 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "AudioChannels", - "Value": StrI(maxAudioChannels), ' Currently Jellyfin server expects this as a string + "Value": maxAudioChannels, "IsRequired": false } ] @@ -200,6 +285,20 @@ function getDeviceProfile() as object } ] } + if addMpeg2Profile + deviceProfile.CodecProfiles.push({ + "Type": "Video", + "Codec": "mpeg2", + "Conditions": [ + { + "Condition": "EqualsAny", + "Property": "VideoLevel", + "Value": mpeg2LevelString, + "IsRequired": false + } + ] + }) + end if if addAv1Profile deviceProfile.CodecProfiles.push({ "Type": "Video", @@ -223,7 +322,7 @@ function getDeviceProfile() as object { "Condition": "EqualsAny", "Property": "VideoProfile", - "Value": "main" + MAIN10, + "Value": hevcProfileString, "IsRequired": false }, { @@ -235,7 +334,7 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "VideoLevel", - "Value": (120 * 5.1).ToStr(), + "Value": (120 * hevcHighestLevel).ToStr(), "IsRequired": false }, GetBitRateLimit("H265") @@ -375,7 +474,7 @@ function GetBitRateLimit(codec as string) "Condition": "LessThanEqual", "Property": "VideoBitrate", "Value": userSetLimit.ToStr(), - IsRequired: true + "IsRequired": true } else ' Some repeated values (e.g. same "40mbps" for several codecs) @@ -386,7 +485,7 @@ function GetBitRateLimit(codec as string) "Condition": "LessThanEqual", "Property": "VideoBitrate", "Value": "10000000", - IsRequired: true + "IsRequired": true } else if codec = "AV1" ' Roku only supports AV1 up to 40Mpbs @@ -394,7 +493,7 @@ function GetBitRateLimit(codec as string) "Condition": "LessThanEqual", "Property": "VideoBitrate", "Value": "40000000", - IsRequired: true + "IsRequired": true } else if codec = "H265" ' Roku only supports h265 up to 40Mpbs @@ -402,7 +501,7 @@ function GetBitRateLimit(codec as string) "Condition": "LessThanEqual", "Property": "VideoBitrate", "Value": "40000000", - IsRequired: true + "IsRequired": true } else if codec = "VP9" ' Roku only supports VP9 up to 40Mpbs @@ -410,7 +509,7 @@ function GetBitRateLimit(codec as string) "Condition": "LessThanEqual", "Property": "VideoBitrate", "Value": "40000000", - IsRequired: true + "IsRequired": true } end if end if From 9c0228a7e8eac16098692285d1f9199726f25565 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 1 Jun 2023 00:35:08 -0400 Subject: [PATCH 02/60] refactor GetDirectPlayProfiles to make no assumptions about codec --- source/utils/deviceCapabilities.brs | 173 +++++++++++++--------------- 1 file changed, 78 insertions(+), 95 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 4872b5cb..b2d26811 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -362,106 +362,89 @@ end function function GetDirectPlayProfiles() as object - - mp4Video = "h264" - mp4Audio = "mp3,pcm,lpcm,wav" - mkvVideo = "h264,vp8" - mkvAudio = "mp3,pcm,lpcm,wav" - audio = "mp3,pcm,lpcm,wav" - - playMpeg2 = get_user_setting("playback.mpeg2") = "true" - di = CreateObject("roDeviceInfo") - - 'Check for Supported Video Codecs - if di.CanDecodeVideo({ Codec: "hevc" }).Result = true - mp4Video = mp4Video + ",h265,hevc" - mkvVideo = mkvVideo + ",h265,hevc" - end if - - if di.CanDecodeVideo({ Codec: "vp9" }).Result = true - mkvVideo = mkvVideo + ",vp9" - end if - - if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2" }).Result = true - mp4Video = mp4Video + ",mpeg2video" - mkvVideo = mkvVideo + ",mpeg2video" - end if - - if get_user_setting("playback.mpeg4") = "true" - mp4Video = mp4Video + ",mpeg4" - end if - - ' Check for supported Audio - if di.CanDecodeAudio({ Codec: "ac3" }).result - mkvAudio = mkvAudio + ",ac3" - mp4Audio = mp4Audio + ",ac3" - audio = audio + ",ac3" - end if - - if di.CanDecodeAudio({ Codec: "wma" }).result - audio = audio + ",wma" - end if - - if di.CanDecodeAudio({ Codec: "flac" }).result - mkvAudio = mkvAudio + ",flac" - audio = audio + ",flac" - end if - - if di.CanDecodeAudio({ Codec: "alac" }).result - mkvAudio = mkvAudio + ",alac" - mp4Audio = mp4Audio + ",alac" - audio = audio + ",alac" - end if - - if di.CanDecodeAudio({ Codec: "aac" }).result - mkvAudio = mkvAudio + ",aac" - mp4Audio = mp4Audio + ",aac" - audio = audio + ",aac" - end if - - if di.CanDecodeAudio({ Codec: "opus" }).result - mkvAudio = mkvAudio + ",opus" - end if - - if di.CanDecodeAudio({ Codec: "dts" }).result - mkvAudio = mkvAudio + ",dts" - audio = audio + ",dts" - end if - - if di.CanDecodeAudio({ Codec: "wmapro" }).result - audio = audio + ",wmapro" - end if - - if di.CanDecodeAudio({ Codec: "vorbis" }).result - mkvAudio = mkvAudio + ",vorbis" - end if - - if di.CanDecodeAudio({ Codec: "eac3" }).result - mkvAudio = mkvAudio + ",eac3" - mp4Audio = mp4Audio + ",eac3" - audio = audio + ",eac3" - end if - - return [ - { - "Container": "mp4,m4v,mov", - "Type": "Video", - "VideoCodec": mp4Video, - "AudioCodec": mp4Audio + ' all possible containers + supportedCodecs = { + mp4: { + audio: [], + video: [] }, - { - "Container": "mkv,webm", - "Type": "Video", - "VideoCodec": mkvVideo, - "AudioCodec": mkvAudio + m4v: { + audio: [], + video: [] }, - { - "Container": audio, - "Type": "Audio" + mov: { + audio: [], + video: [] + }, + mkv: { + audio: [], + video: [] + }, + webm: { + audio: [], + video: [] } - ] + } + ' all possible codecs + videoCodecs = ["h264", "vp8", "hevc", "vp9"] + audioCodecs = ["mp3", "pcm", "lpcm", "wav", "ac3", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] + ' respect user settings + if get_user_setting("playback.mpeg4") = "true" + videoCodecs.push("mpeg4") + end if + if get_user_setting("playback.mpeg2") = "true" + videoCodecs.push("mpeg2") + end if + ' check video codecs for each container + for each container in supportedCodecs + for each videoCodec in videoCodecs + if di.CanDecodeVideo({ Codec: videoCodec, Container: container }).Result + if videoCodec = "hevc" + supportedCodecs[container]["video"].push("hevc") + supportedCodecs[container]["video"].push("h265") + else if videoCodec = "mpeg2" + supportedCodecs[container]["video"].push("mpeg2video") + else + ' device profile string matches codec string + supportedCodecs[container]["video"].push(videoCodec) + end if + end if + end for + end for + ' check audio codecs for each container + for each container in supportedCodecs + for each audioCodec in audioCodecs + if di.CanDecodeAudio({ Codec: audioCodec, Container: container }).Result + supportedCodecs[container]["audio"].push(audioCodec) + end if + end for + end for + ' check audio codecs with no container + supportedAudio = [] + for each audioCodec in audioCodecs + if di.CanDecodeAudio({ Codec: audioCodec }).Result + supportedAudio.push(audioCodec) + end if + end for + returnArray = [] + for each container in supportedCodecs + if supportedCodecs[container]["video"].Join(",") <> "" + returnArray.push({ + "Container": container, + "Type": "Video", + "VideoCodec": supportedCodecs[container]["video"].Join(","), + "AudioCodec": supportedCodecs[container]["audio"].Join(",") + }) + end if + end for + + returnArray.push({ + "Container": supportedAudio.Join(","), + "Type": "Audio" + }) + return returnArray end function function GetBitRateLimit(codec as string) From 0027295dadd51a7adb1c0173d8a9754d78edf5d2 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 1 Jun 2023 00:37:04 -0400 Subject: [PATCH 03/60] save to variable to prevent calling join() twice --- source/utils/deviceCapabilities.brs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index b2d26811..25013bd3 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -430,11 +430,12 @@ function GetDirectPlayProfiles() as object returnArray = [] for each container in supportedCodecs - if supportedCodecs[container]["video"].Join(",") <> "" + videoCodecString = supportedCodecs[container]["video"].Join(",") + if videoCodecString <> "" returnArray.push({ "Container": container, "Type": "Video", - "VideoCodec": supportedCodecs[container]["video"].Join(","), + "VideoCodec": videoCodecString, "AudioCodec": supportedCodecs[container]["audio"].Join(",") }) end if From bb499cabd39de91073a26f7634adb1d1a7ebfe90 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 1 Jun 2023 00:38:31 -0400 Subject: [PATCH 04/60] turn SupportsPersistentIdentifier off --- source/utils/deviceCapabilities.brs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 25013bd3..f39004be 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -9,7 +9,7 @@ function getDeviceCapabilities() as object "Photo" ], "SupportedCommands": [], - "SupportsPersistentIdentifier": true, + "SupportsPersistentIdentifier": false, "SupportsMediaControl": false, "DeviceProfile": getDeviceProfile() } From 8c3605c01a43f29f8dccec31283defd809526122 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 2 Jun 2023 00:37:22 -0400 Subject: [PATCH 05/60] include video codec profile and level + include chCnt for audio codec --- source/utils/deviceCapabilities.brs | 624 ++++++++++++++++++++-------- 1 file changed, 458 insertions(+), 166 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 998e20d3..11467503 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -52,104 +52,264 @@ function getDeviceProfile() as object playAv1 = m.global.session.user.settings["playback.av1"] di = CreateObject("roDeviceInfo") - maxAudioChannels = "2" ' Currently Jellyfin server expects this as a string - tsVideoCodecs = "h264" - tsAudioCodecs = "aac" - - 'Check if 5.1 Audio Output connected - if di.GetAudioOutputChannel() = "5.1 surround" - maxAudioChannels = "6" - end if - - ' HEVC + audioChannelIntegers = [ + 2, ' stereo + 6, ' 5.1 channel + 8 ' 7.1 channel + ] + transContainers = ["mp4", "hls", "mkv", "ism", "dash", "ts"] + supportedVideoCodecs = {} + supportedAudioCodecs = {} + addH264Profile = false addHevcProfile = false - hevcProfileString = "" - hevcHighestLevel = 4.1 + addMpeg2Profile = false + addAv1Profile = false + addVp9Profile = false - if di.CanDecodeVideo({ Codec: "hevc", Container: "ts" }).Result = true - tsVideoCodecs = "h265,hevc," + tsVideoCodecs - addHevcProfile = true + ' AVC / h264 + h264Profiles = ["main", "high"] + h264Levels = ["4.1", "4.2"] - hevcProfiles = ["main", "main 10"] - hevcLevels = ["4.1", "5.0", "5.1"] - supportArray = {} - - for each profile in hevcProfiles - for each level in hevcLevels - if di.CanDecodeVideo({ Codec: "hevc", Container: "ts", Profile: profile, Level: level }).Result - if supportArray[profile] = invalid - supportArray[profile] = [] - if hevcProfileString = "" - hevcProfileString = profile - else - hevcProfileString = hevcProfileString + "|" + profile - end if + for each container in transContainers + for each profile in h264Profiles + for each level in h264Levels + if di.CanDecodeVideo({ Codec: "h264", Container: container, Profile: profile, Level: level }).Result + addH264Profile = true + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} end if - - supportArray[profile].Push(level) + if supportedVideoCodecs[container]["h264"] = invalid + supportedVideoCodecs[container]["h264"] = {} + end if + if supportedVideoCodecs[container]["h264"][profile] = invalid + supportedVideoCodecs[container]["h264"][profile] = [] + end if + supportedVideoCodecs[container]["h264"][profile].push(level) end if end for end for + end for - for each prof in supportArray - highestLevelString = supportArray[prof].Pop() - if highestLevelString = "5" - hevcHighestLevel = 5 - end if - if highestLevelString = "5.1" - hevcHighestLevel = 5.1 - end if + ' HEVC / h265 + hevcProfiles = ["main", "main 10"] + hevcLevels = ["4.1", "5.0", "5.1"] + + for each container in transContainers + for each profile in hevcProfiles + for each level in hevcLevels + if di.CanDecodeVideo({ Codec: "hevc", Container: container, Profile: profile, Level: level }).Result + addHevcProfile = true + ' hevc codec string + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} + end if + if supportedVideoCodecs[container]["hevc"] = invalid + supportedVideoCodecs[container]["hevc"] = {} + end if + if supportedVideoCodecs[container]["hevc"][profile] = invalid + supportedVideoCodecs[container]["hevc"][profile] = [] + end if + supportedVideoCodecs[container]["hevc"][profile].push(level) + ' h265 codec string + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} + end if + if supportedVideoCodecs[container]["h265"] = invalid + supportedVideoCodecs[container]["h265"] = {} + end if + if supportedVideoCodecs[container]["h265"][profile] = invalid + supportedVideoCodecs[container]["h265"][profile] = [] + end if + supportedVideoCodecs[container]["h265"][profile].push(level) + end if + end for + end for + end for + + ' MPEG2 + mpeg2Levels = ["main", "high"] + if playMpeg2 + for each container in transContainers + for each level in mpeg2Levels + if di.CanDecodeVideo({ Codec: "mpeg2", Container: container, Level: level }).Result + addMpeg2Profile = true + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} + end if + if supportedVideoCodecs[container]["mpeg2"] = invalid + supportedVideoCodecs[container]["mpeg2"] = [] + end if + supportedVideoCodecs[container]["mpeg2"].push(level) + end if + end for end for end if - ' MPEG2 - addMpeg2Profile = false - mpeg2LevelString = "" - if playMpeg2 and di.CanDecodeVideo({ Codec: "mpeg2", Container: "ts" }).Result = true - tsVideoCodecs = tsVideoCodecs + ",mpeg2video" - addMpeg2Profile = true + ' AV1 + av1Profiles = ["main", "main 10"] + av1Levels = ["4.1", "5.0", "5.1"] + if playAv1 + for each container in transContainers + for each profile in av1Profiles + for each level in av1Levels + if di.CanDecodeVideo({ Codec: "av1", Container: container, Profile: profile, Level: level }).Result + addAv1Profile = true + ' av1 codec string + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} + end if + if supportedVideoCodecs[container]["av1"] = invalid + supportedVideoCodecs[container]["av1"] = {} + end if + if supportedVideoCodecs[container]["av1"][profile] = invalid + supportedVideoCodecs[container]["av1"][profile] = [] + end if + supportedVideoCodecs[container]["av1"][profile].push(level) + end if + end for + end for + end for + end if - mpeg2Levels = ["main", "high"] + ' VP9 + vp9Profiles = ["profile 0", "profile 2"] - for each level in mpeg2Levels - if di.CanDecodeVideo({ Codec: "mpeg2", Container: "ts", Level: level }).Result - if mpeg2LevelString = "" - mpeg2LevelString = level - else - mpeg2LevelString = mpeg2LevelString + "|" + level + for each container in transContainers + for each profile in vp9Profiles + if di.CanDecodeVideo({ Codec: "vp9", Container: container, Profile: profile }).Result + addVp9Profile = true + ' vp9 codec string + if supportedVideoCodecs[container] = invalid + supportedVideoCodecs[container] = {} + end if + if supportedVideoCodecs[container]["vp9"] = invalid + supportedVideoCodecs[container]["vp9"] = [] + end if + supportedVideoCodecs[container]["vp9"].push(profile) + end if + end for + end for + + ' eac3 + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "eac3", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["eac3"] = invalid or audioChannel > supportedAudioCodecs[container]["eac3"] + supportedAudioCodecs[container]["eac3"] = audioChannel end if end if end for - end if + end for - if di.CanDecodeAudio({ Codec: "mp3", Container: "ts" }).result - tsAudioCodecs = tsAudioCodecs + ",mp3" - end if - if di.CanDecodeAudio({ Codec: "dts", Container: "ts" }).result - tsAudioCodecs = "dts," + tsAudioCodecs - end if + ' ac3 + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "ac3", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if - if di.CanDecodeAudio({ Codec: "ac3", Container: "ts" }).result - tsAudioCodecs = "ac3," + tsAudioCodecs - end if + if supportedAudioCodecs[container]["ac3"] = invalid or audioChannel > supportedAudioCodecs[container]["ac3"] + supportedAudioCodecs[container]["ac3"] = audioChannel + end if + end if + end for + end for - ' prefer eac3 over all other audio codecs - if di.CanDecodeAudio({ Codec: "eac3", Container: "ts" }).result - tsAudioCodecs = "eac3," + tsAudioCodecs - end if + ' dts + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "dts", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if - addAv1Profile = false - if playAv1 and di.CanDecodeVideo({ Codec: "av1", Container: "ts" }).result - tsVideoCodecs = tsVideoCodecs + ",av1" - addAv1Profile = true - end if + if supportedAudioCodecs[container]["dts"] = invalid or audioChannel > supportedAudioCodecs[container]["dts"] + supportedAudioCodecs[container]["dts"] = audioChannel + end if + end if + end for + end for - addVp9Profile = false - if di.CanDecodeVideo({ Codec: "vp9", Container: "ts" }).result - tsVideoCodecs = tsVideoCodecs + ",vp9" - addVp9Profile = true - end if + ' opus + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "opus", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["opus"] = invalid or audioChannel > supportedAudioCodecs[container]["opus"] + supportedAudioCodecs[container]["opus"] = audioChannel + end if + end if + end for + end for + + ' flac + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "flac", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["flac"] = invalid or audioChannel > supportedAudioCodecs[container]["flac"] + supportedAudioCodecs[container]["flac"] = audioChannel + end if + end if + end for + end for + + ' vorbis + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "vorbis", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["vorbis"] = invalid or audioChannel > supportedAudioCodecs[container]["vorbis"] + supportedAudioCodecs[container]["vorbis"] = audioChannel + end if + end if + end for + end for + + ' aac + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "aac", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["aac"] = invalid or audioChannel > supportedAudioCodecs[container]["aac"] + supportedAudioCodecs[container]["aac"] = audioChannel + end if + end if + end for + end for + + ' mp3 + for each container in transContainers + for each audioChannel in audioChannelIntegers + if di.CanDecodeAudio({ Codec: "mp3", Container: container, ChCnt: audioChannel }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = {} + end if + + if supportedAudioCodecs[container]["mp3"] = invalid or audioChannel > supportedAudioCodecs[container]["mp3"] + supportedAudioCodecs[container]["mp3"] = audioChannel + end if + end if + end for + end for hevcVideoRangeTypes = "SDR" vp9VideoRangeTypes = "SDR" @@ -179,59 +339,7 @@ function getDeviceProfile() as object "MaxStaticBitrate": 100000000, "MusicStreamingTranscodingBitrate": 192000, "DirectPlayProfiles": DirectPlayProfile, - "TranscodingProfiles": [ - { - "Container": "aac", - "Type": "Audio", - "AudioCodec": "aac", - "Context": "Streaming", - "Protocol": "http", - "MaxAudioChannels": maxAudioChannels - }, - { - "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", - "MaxAudioChannels": maxAudioChannels - }, - { - "Container": "ts", - "Type": "Video", - "AudioCodec": tsAudioCodecs, - "VideoCodec": tsVideoCodecs, - "Context": "Streaming", - "Protocol": "hls", - "MaxAudioChannels": maxAudioChannels, - "MinSegments": "1", - "BreakOnNonKeyFrames": true - }, - { - "Container": "mp4", - "Type": "Video", - "AudioCodec": "aac,opus,flac,vorbis", - "VideoCodec": "h264", - "Context": "Static", - "Protocol": "http" - } - ], + "TranscodingProfiles": [], "ContainerProfiles": [], "CodecProfiles": [ { @@ -241,29 +349,10 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "AudioChannels", - "Value": maxAudioChannels, + "Value": supportedAudioCodecs["mkv"]["flac"].ToStr(), "IsRequired": false } ] - }, - { - "Type": "Video", - "Codec": "h264", - "Conditions": [ - { - "Condition": "EqualsAny", - "Property": "VideoProfile", - "Value": "high|main", - "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "VideoLevel", - "Value": "41", - "IsRequired": false - }, - GetBitRateLimit("H264") - ] } ], "SubtitleProfiles": [ @@ -285,7 +374,121 @@ function getDeviceProfile() as object } ] } + + ' build TranscodingProfiles + ' create an audio profile for each audio codec supported by the mp4 container + for each supportedMp4AudioCodec in supportedAudioCodecs["mp4"] + ' streaming + deviceProfile.TranscodingProfiles.push({ + "Container": supportedMp4AudioCodec, + "Type": "Audio", + "AudioCodec": supportedMp4AudioCodec, + "Context": "Streaming", + "Protocol": "http", + "MaxAudioChannels": supportedAudioCodecs["mp4"][supportedMp4AudioCodec].ToStr() + }) + ' static + deviceProfile.TranscodingProfiles.push({ + "Container": supportedMp4AudioCodec, + "Type": "Audio", + "AudioCodec": supportedMp4AudioCodec, + "Context": "Static", + "Protocol": "http", + "MaxAudioChannels": supportedAudioCodecs["mp4"][supportedMp4AudioCodec].ToStr() + }) + end for + ' create a video profile for each container in transContainers + for each container in transContainers + audioCodecs = [] + videoCodecs = [] + for each codec in supportedAudioCodecs[container] + audioCodecs.push(codec) + end for + for each codec in supportedVideoCodecs[container] + videoCodecs.push(codec) + end for + containerArray = { + "Container": container, + "Context": "Static", + "Type": "Video", + "AudioCodec": audioCodecs.join(","), + "VideoCodec": videoCodecs.join(",") + } + ' grab max audio channels based on container + ' order of priority: ac3, aac + if supportedAudioCodecs[container]["ac3"] <> invalid + containerArray["MaxAudioChannels"] = supportedAudioCodecs[container]["ac3"].ToStr() + else + containerArray["MaxAudioChannels"] = supportedAudioCodecs[container]["aac"].ToStr() + end if + + if container = "ts" + containerArray["Context"] = "Streaming" + containerArray["Protocol"] = "hls" + containerArray["MinSegments"] = "1" + containerArray["BreakOnNonKeyFrames"] = true + else if container = "mp4" + containerArray["Context"] = "Static" + containerArray["Protocol"] = "http" + end if + deviceProfile.TranscodingProfiles.push(containerArray) + end for + + ' build CodecProfiles + if addH264Profile + ' determine highest level supported + h264HighestLevel = 0 + for each profile in h264Profiles + for each level in supportedVideoCodecs["ts"]["h264"][profile] + levelFloat = level.ToFloat() + if levelFloat > h264HighestLevel + h264HighestLevel = levelFloat + end if + end for + end for + + videoProfiles = [] + for each container in transContainers + if supportedVideoCodecs[container]["h264"] <> invalid + for each profile in supportedVideoCodecs[container]["h264"] + videoProfiles.push(profile) + end for + exit for + end if + end for + + deviceProfile.CodecProfiles.push({ + "Type": "Video", + "Codec": "h264", + "Conditions": [ + { + "Condition": "EqualsAny", + "Property": "VideoProfile", + "Value": videoProfiles.join("|"), + "IsRequired": false + }, + { + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": (120 * h264HighestLevel).ToStr(), + "IsRequired": false + }, + GetBitRateLimit("h264") + ] + }) + end if if addMpeg2Profile + mpeg2Levels = [] + for each container in transContainers + if supportedVideoCodecs[container] <> invalid + if supportedVideoCodecs[container]["mpeg2"] <> invalid + for each level in supportedVideoCodecs[container]["mpeg2"] + mpeg2Levels.push(level) + end for + if mpeg2Levels.count > 0 then exit for + end if + end if + end for deviceProfile.CodecProfiles.push({ "Type": "Video", "Codec": "mpeg2", @@ -293,28 +496,85 @@ function getDeviceProfile() as object { "Condition": "EqualsAny", "Property": "VideoLevel", - "Value": mpeg2LevelString, + "Value": mpeg2Levels.join("|"), "IsRequired": false - } + }, + GetBitRateLimit("mpeg2") ] }) end if + if addAv1Profile + ' determine highest level supported + av1HighestLevel = 0.0 + for each profile in av1Profiles + for each level in supportedVideoCodecs["ts"]["av1"][profile] + if level.ToFloat() > av1HighestLevel.ToFloat() + h264HighestLevel = level + end if + end for + end for + + videoProfiles = [] + for each container in transContainers + if supportedVideoCodecs[container]["av1"] <> invalid + for each profile in supportedVideoCodecs[container]["av1"] + videoProfiles.push(profile) + end for + exit for + end if + end for + deviceProfile.CodecProfiles.push({ "Type": "Video", "Codec": "av1", "Conditions": [ + { + "Condition": "EqualsAny", + "Property": "VideoProfile", + "Value": videoProfiles.join("|"), + "IsRequired": false + }, { "Condition": "EqualsAny", "Property": "VideoRangeType", "Value": av1VideoRangeTypes, "IsRequired": false }, + { + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": (120 * av1HighestLevel).ToStr(), + "IsRequired": false + }, GetBitRateLimit("AV1") ] }) end if + if addHevcProfile + ' determine highest level supported + hevcHighestLevel = 0.0 + for each profile in hevcProfiles + for each level in supportedVideoCodecs["ts"]["hevc"][profile] + levelFloat = level.ToFloat() + if levelFloat > hevcHighestLevel + hevcHighestLevel = levelFloat + end if + end for + end for + + videoProfiles = [] + for each container in transContainers + if supportedVideoCodecs[container]["hevc"] <> invalid + for each profile in supportedVideoCodecs[container]["hevc"] + videoProfiles.push(profile) + end for + exit for + end if + end for + + ' use ts container codecs deviceProfile.CodecProfiles.push({ "Type": "Video", "Codec": "hevc", @@ -322,7 +582,7 @@ function getDeviceProfile() as object { "Condition": "EqualsAny", "Property": "VideoProfile", - "Value": hevcProfileString, + "Value": videoProfiles.join("|"), "IsRequired": false }, { @@ -341,18 +601,36 @@ function getDeviceProfile() as object ] }) end if + if addVp9Profile + videoProfiles = [] + for each container in transContainers + if supportedVideoCodecs[container]["vp9"] <> invalid + for each profile in supportedVideoCodecs[container]["vp9"] + videoProfiles.push(profile) + end for + exit for + end if + end for + + ' use ts container codecs deviceProfile.CodecProfiles.push({ "Type": "Video", "Codec": "vp9", "Conditions": [ + { + "Condition": "EqualsAny", + "Property": "VideoProfile", + "Value": videoProfiles.join("|"), + "IsRequired": false + }, { "Condition": "EqualsAny", "Property": "VideoRangeType", "Value": vp9VideoRangeTypes, "IsRequired": false }, - GetBitRateLimit("VP9") + GetBitRateLimit("vp9") ] }) end if @@ -369,11 +647,7 @@ function GetDirectPlayProfiles() as object audio: [], video: [] }, - m4v: { - audio: [], - video: [] - }, - mov: { + hls: { audio: [], video: [] }, @@ -381,7 +655,15 @@ function GetDirectPlayProfiles() as object audio: [], video: [] }, - webm: { + ism: { + audio: [], + video: [] + }, + dash: { + audio: [], + video: [] + }, + ts: { audio: [], video: [] } @@ -427,13 +709,22 @@ function GetDirectPlayProfiles() as object supportedAudio.push(audioCodec) end if end for - + ' build return array returnArray = [] + for each container in supportedCodecs videoCodecString = supportedCodecs[container]["video"].Join(",") if videoCodecString <> "" + containerString = "" + if container = "mp4" + containerString = "mp4,mov,m4v" + else if container = "mkv" + containerString = "mkv,webm" + else + containerString = container + end if returnArray.push({ - "Container": container, + "Container": containerString, "Type": "Video", "VideoCodec": videoCodecString, "AudioCodec": supportedCodecs[container]["audio"].Join(",") @@ -448,7 +739,7 @@ function GetDirectPlayProfiles() as object return returnArray end function -function GetBitRateLimit(codec as string) +function GetBitRateLimit(codec as string) as object if m.global.session.user.settings["playback.bitrate.maxlimited"] = true userSetLimit = m.global.session.user.settings["playback.bitrate.limit"] userSetLimit *= 1000000 @@ -461,9 +752,10 @@ function GetBitRateLimit(codec as string) "IsRequired": true } else + codec = Lcase(codec) ' 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" + if codec = "h264" ' Roku only supports h264 up to 10Mpbs return { "Condition": "LessThanEqual", @@ -471,7 +763,7 @@ function GetBitRateLimit(codec as string) "Value": "10000000", "IsRequired": true } - else if codec = "AV1" + else if codec = "av1" ' Roku only supports AV1 up to 40Mpbs return { "Condition": "LessThanEqual", @@ -479,7 +771,7 @@ function GetBitRateLimit(codec as string) "Value": "40000000", "IsRequired": true } - else if codec = "H265" + else if codec = "h265" ' Roku only supports h265 up to 40Mpbs return { "Condition": "LessThanEqual", @@ -487,7 +779,7 @@ function GetBitRateLimit(codec as string) "Value": "40000000", "IsRequired": true } - else if codec = "VP9" + else if codec = "vp9" ' Roku only supports VP9 up to 40Mpbs return { "Condition": "LessThanEqual", From 56bb2108b58035661e656cd820a456f69617a380 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 2 Jun 2023 10:26:49 -0400 Subject: [PATCH 06/60] fix syntax error --- source/utils/deviceCapabilities.brs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 11467503..47b34583 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -485,7 +485,7 @@ function getDeviceProfile() as object for each level in supportedVideoCodecs[container]["mpeg2"] mpeg2Levels.push(level) end for - if mpeg2Levels.count > 0 then exit for + if mpeg2Levels.count() > 0 then exit for end if end if end for From adc769c6253c7f1a0beeac23ce6ac0cce0396880 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 2 Jun 2023 23:47:16 -0400 Subject: [PATCH 07/60] update maxAudioChannel logic + update codecProfiles --- source/utils/deviceCapabilities.brs | 340 ++++++++++++++++------------ 1 file changed, 191 insertions(+), 149 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 47b34583..26ca3444 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -52,11 +52,6 @@ function getDeviceProfile() as object playAv1 = m.global.session.user.settings["playback.av1"] di = CreateObject("roDeviceInfo") - audioChannelIntegers = [ - 2, ' stereo - 6, ' 5.1 channel - 8 ' 7.1 channel - ] transContainers = ["mp4", "hls", "mkv", "ism", "dash", "ts"] supportedVideoCodecs = {} supportedAudioCodecs = {} @@ -66,6 +61,19 @@ function getDeviceProfile() as object addAv1Profile = false addVp9Profile = false + maxAudioChannels = "2" + di = CreateObject("roDeviceInfo") + if di.GetAudioOutputChannel() = "5.1 surround" + maxAudioChannels = "6" + eightChannelCodecs = ["ac3", "eac3", "dts"] + for each eightChannelCodec in eightChannelCodecs + if di.CanDecodeAudio({ Codec: eightChannelCodec, ChCnt: 8 }).Result + maxAudioChannels = "8" + exit for + end if + end for + end if + ' AVC / h264 h264Profiles = ["main", "high"] h264Levels = ["4.1", "4.2"] @@ -136,10 +144,10 @@ function getDeviceProfile() as object if supportedVideoCodecs[container] = invalid supportedVideoCodecs[container] = {} end if - if supportedVideoCodecs[container]["mpeg2"] = invalid - supportedVideoCodecs[container]["mpeg2"] = [] + if supportedVideoCodecs[container]["mpeg2video"] = invalid + supportedVideoCodecs[container]["mpeg2video"] = [] end if - supportedVideoCodecs[container]["mpeg2"].push(level) + supportedVideoCodecs[container]["mpeg2video"].push(level) end if end for end for @@ -192,123 +200,91 @@ function getDeviceProfile() as object ' eac3 for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "eac3", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["eac3"] = invalid or audioChannel > supportedAudioCodecs[container]["eac3"] - supportedAudioCodecs[container]["eac3"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "eac3", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("eac3") + end if end for ' ac3 for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "ac3", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["ac3"] = invalid or audioChannel > supportedAudioCodecs[container]["ac3"] - supportedAudioCodecs[container]["ac3"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "ac3", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("ac3") + end if end for ' dts for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "dts", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["dts"] = invalid or audioChannel > supportedAudioCodecs[container]["dts"] - supportedAudioCodecs[container]["dts"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "dts", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("dts") + end if end for ' opus for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "opus", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["opus"] = invalid or audioChannel > supportedAudioCodecs[container]["opus"] - supportedAudioCodecs[container]["opus"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "opus", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("opus") + end if end for ' flac for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "flac", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["flac"] = invalid or audioChannel > supportedAudioCodecs[container]["flac"] - supportedAudioCodecs[container]["flac"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "flac", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("flac") + end if end for ' vorbis for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "vorbis", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["vorbis"] = invalid or audioChannel > supportedAudioCodecs[container]["vorbis"] - supportedAudioCodecs[container]["vorbis"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "vorbis", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("vorbis") + end if end for ' aac for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "aac", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["aac"] = invalid or audioChannel > supportedAudioCodecs[container]["aac"] - supportedAudioCodecs[container]["aac"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "aac", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("aac") + end if end for ' mp3 for each container in transContainers - for each audioChannel in audioChannelIntegers - if di.CanDecodeAudio({ Codec: "mp3", Container: container, ChCnt: audioChannel }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = {} - end if - - if supportedAudioCodecs[container]["mp3"] = invalid or audioChannel > supportedAudioCodecs[container]["mp3"] - supportedAudioCodecs[container]["mp3"] = audioChannel - end if + if di.CanDecodeAudio({ Codec: "mp3", Container: container }).result + if supportedAudioCodecs[container] = invalid + supportedAudioCodecs[container] = [] end if - end for + + supportedAudioCodecs[container].push("mp3") + end if end for hevcVideoRangeTypes = "SDR" @@ -349,7 +325,7 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "AudioChannels", - "Value": supportedAudioCodecs["mkv"]["flac"].ToStr(), + "Value": maxAudioChannels, "IsRequired": false } ] @@ -385,7 +361,7 @@ function getDeviceProfile() as object "AudioCodec": supportedMp4AudioCodec, "Context": "Streaming", "Protocol": "http", - "MaxAudioChannels": supportedAudioCodecs["mp4"][supportedMp4AudioCodec].ToStr() + "MaxAudioChannels": maxAudioChannels }) ' static deviceProfile.TranscodingProfiles.push({ @@ -394,7 +370,7 @@ function getDeviceProfile() as object "AudioCodec": supportedMp4AudioCodec, "Context": "Static", "Protocol": "http", - "MaxAudioChannels": supportedAudioCodecs["mp4"][supportedMp4AudioCodec].ToStr() + "MaxAudioChannels": maxAudioChannels }) end for ' create a video profile for each container in transContainers @@ -412,15 +388,9 @@ function getDeviceProfile() as object "Context": "Static", "Type": "Video", "AudioCodec": audioCodecs.join(","), - "VideoCodec": videoCodecs.join(",") + "VideoCodec": videoCodecs.join(","), + "MaxAudioChannels": maxAudioChannels } - ' grab max audio channels based on container - ' order of priority: ac3, aac - if supportedAudioCodecs[container]["ac3"] <> invalid - containerArray["MaxAudioChannels"] = supportedAudioCodecs[container]["ac3"].ToStr() - else - containerArray["MaxAudioChannels"] = supportedAudioCodecs[container]["aac"].ToStr() - end if if container = "ts" containerArray["Context"] = "Streaming" @@ -437,16 +407,28 @@ function getDeviceProfile() as object ' build CodecProfiles if addH264Profile ' determine highest level supported - h264HighestLevel = 0 - for each profile in h264Profiles - for each level in supportedVideoCodecs["ts"]["h264"][profile] - levelFloat = level.ToFloat() - if levelFloat > h264HighestLevel - h264HighestLevel = levelFloat - end if + h264HighestLevel = 4.2 + h264HighestLevelSupported = 0.0 + for each container in transContainers + for each profile in hevcProfiles + for each level in supportedVideoCodecs[container]["h264"][profile] + levelFloat = level.ToFloat() + if levelFloat > h264HighestLevelSupported + h264HighestLevelSupported = levelFloat + end if + if h264HighestLevelSupported = h264HighestLevel then exit for + end for + if h264HighestLevelSupported = h264HighestLevel then exit for end for + if h264HighestLevelSupported = h264HighestLevel then exit for end for + h264LevelString = "41" + if h264HighestLevelSupported = 4.2 + h264LevelString = "42" + end if + + videoProfiles = [] for each container in transContainers if supportedVideoCodecs[container]["h264"] <> invalid @@ -457,10 +439,16 @@ function getDeviceProfile() as object end if end for - deviceProfile.CodecProfiles.push({ + codecProfileArray = { "Type": "Video", "Codec": "h264", "Conditions": [ + { + "Condition": "NotEquals", + "Property": "IsAnamorphic", + "Value": "true", + "IsRequired": false + }, { "Condition": "EqualsAny", "Property": "VideoProfile", @@ -468,15 +456,26 @@ function getDeviceProfile() as object "IsRequired": false }, { - "Condition": "LessThanEqual", - "Property": "VideoLevel", - "Value": (120 * h264HighestLevel).ToStr(), + "Condition": "EqualsAny", + "Property": "VideoRangeType", + "Value": "SDR", "IsRequired": false }, - GetBitRateLimit("h264") + { + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": h264LevelString, + "IsRequired": false + } ] - }) + } + bitRateArray = GetBitRateLimit("h264") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if + if addMpeg2Profile mpeg2Levels = [] for each container in transContainers @@ -489,7 +488,8 @@ function getDeviceProfile() as object end if end if end for - deviceProfile.CodecProfiles.push({ + + codecProfileArray = { "Type": "Video", "Codec": "mpeg2", "Conditions": [ @@ -498,21 +498,32 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": mpeg2Levels.join("|"), "IsRequired": false - }, - GetBitRateLimit("mpeg2") + } ] - }) + } + bitRateArray = GetBitRateLimit("mpeg2") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if if addAv1Profile ' determine highest level supported - av1HighestLevel = 0.0 - for each profile in av1Profiles - for each level in supportedVideoCodecs["ts"]["av1"][profile] - if level.ToFloat() > av1HighestLevel.ToFloat() - h264HighestLevel = level - end if + av1HighestLevel = 5.1 + av1HighestLevelSupported = 0.0 + for each container in transContainers + for each profile in hevcProfiles + for each level in supportedVideoCodecs[container]["av1"][profile] + levelFloat = level.ToFloat() + if levelFloat > av1HighestLevelSupported + av1HighestLevelSupported = levelFloat + end if + if av1HighestLevelSupported = av1HighestLevel then exit for + end for + if av1HighestLevelSupported = av1HighestLevel then exit for end for + if av1HighestLevelSupported = av1HighestLevel then exit for end for videoProfiles = [] @@ -525,7 +536,7 @@ function getDeviceProfile() as object end if end for - deviceProfile.CodecProfiles.push({ + codecProfileArray = { "Type": "Video", "Codec": "av1", "Conditions": [ @@ -544,26 +555,41 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "VideoLevel", - "Value": (120 * av1HighestLevel).ToStr(), + "Value": (120 * av1HighestLevelSupported).ToStr(), "IsRequired": false - }, - GetBitRateLimit("AV1") + } ] - }) + } + bitRateArray = GetBitRateLimit("av1") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if if addHevcProfile ' determine highest level supported - hevcHighestLevel = 0.0 - for each profile in hevcProfiles - for each level in supportedVideoCodecs["ts"]["hevc"][profile] - levelFloat = level.ToFloat() - if levelFloat > hevcHighestLevel - hevcHighestLevel = levelFloat - end if + hevcHighestLevel = 5.1 + hevcHighestLevelSupported = 0.0 + for each container in transContainers + for each profile in hevcProfiles + for each level in supportedVideoCodecs[container]["hevc"][profile] + levelFloat = level.ToFloat() + if levelFloat > hevcHighestLevelSupported + hevcHighestLevelSupported = levelFloat + end if + if hevcHighestLevelSupported = hevcHighestLevel then exit for + end for + if hevcHighestLevelSupported = hevcHighestLevel then exit for end for + if hevcHighestLevelSupported = hevcHighestLevel then exit for end for + hevcLevelString = "120" + if hevcHighestLevelSupported = 5.1 + hevcLevelString = "153" + end if + videoProfiles = [] for each container in transContainers if supportedVideoCodecs[container]["hevc"] <> invalid @@ -574,11 +600,16 @@ function getDeviceProfile() as object end if end for - ' use ts container codecs - deviceProfile.CodecProfiles.push({ + codecProfileArray = { "Type": "Video", "Codec": "hevc", "Conditions": [ + { + "Condition": "NotEquals", + "Property": "IsAnamorphic", + "Value": "true", + "IsRequired": false + }, { "Condition": "EqualsAny", "Property": "VideoProfile", @@ -594,12 +625,16 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "VideoLevel", - "Value": (120 * hevcHighestLevel).ToStr(), + "Value": hevcLevelString, "IsRequired": false - }, - GetBitRateLimit("H265") + } ] - }) + } + bitRateArray = GetBitRateLimit("h265") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if if addVp9Profile @@ -613,8 +648,7 @@ function getDeviceProfile() as object end if end for - ' use ts container codecs - deviceProfile.CodecProfiles.push({ + codecProfileArray = { "Type": "Video", "Codec": "vp9", "Conditions": [ @@ -629,10 +663,14 @@ function getDeviceProfile() as object "Property": "VideoRangeType", "Value": vp9VideoRangeTypes, "IsRequired": false - }, - GetBitRateLimit("vp9") + } ] - }) + } + bitRateArray = GetBitRateLimit("vp9") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if return deviceProfile @@ -671,6 +709,7 @@ function GetDirectPlayProfiles() as object ' all possible codecs videoCodecs = ["h264", "vp8", "hevc", "vp9"] audioCodecs = ["mp3", "pcm", "lpcm", "wav", "ac3", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] + ' respect user settings if m.global.session.user.settings["playback.mpeg4"] videoCodecs.push("mpeg4") @@ -678,6 +717,7 @@ function GetDirectPlayProfiles() as object if m.global.session.user.settings["playback.mpeg2"] videoCodecs.push("mpeg2") end if + ' check video codecs for each container for each container in supportedCodecs for each videoCodec in videoCodecs @@ -694,6 +734,7 @@ function GetDirectPlayProfiles() as object end if end for end for + ' check audio codecs for each container for each container in supportedCodecs for each audioCodec in audioCodecs @@ -702,6 +743,7 @@ function GetDirectPlayProfiles() as object end if end for end for + ' check audio codecs with no container supportedAudio = [] for each audioCodec in audioCodecs @@ -709,20 +751,20 @@ function GetDirectPlayProfiles() as object supportedAudio.push(audioCodec) end if end for + ' build return array returnArray = [] - for each container in supportedCodecs videoCodecString = supportedCodecs[container]["video"].Join(",") if videoCodecString <> "" - containerString = "" + containerString = container + if container = "mp4" containerString = "mp4,mov,m4v" else if container = "mkv" containerString = "mkv,webm" - else - containerString = container end if + returnArray.push({ "Container": containerString, "Type": "Video", From 9c6201b25eb5048ae52888fddbb5ec00e846871e Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 6 Jun 2023 00:24:54 -0400 Subject: [PATCH 08/60] update prefered codecs and maxAudioChannel logic --- source/utils/deviceCapabilities.brs | 765 +++++++++++++++------------- 1 file changed, 397 insertions(+), 368 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 26ca3444..3c15d366 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -52,47 +52,63 @@ function getDeviceProfile() as object playAv1 = m.global.session.user.settings["playback.av1"] di = CreateObject("roDeviceInfo") - transContainers = ["mp4", "hls", "mkv", "ism", "dash", "ts"] - supportedVideoCodecs = {} - supportedAudioCodecs = {} - addH264Profile = false - addHevcProfile = false - addMpeg2Profile = false - addAv1Profile = false - addVp9Profile = false + ' TRANSCODING + ' use strings to preserve order + mp4AudioCodecs = "aac" + mp4VideoCodecs = "h264" + tsAudioCodecs = "aac" + tsVideoCodecs = "h264" + ' profileSupport["mp4"]["hevc"]["profile name"]["profile level"] + profileSupport = { + mp4: {}, + ts: {} + } - maxAudioChannels = "2" - di = CreateObject("roDeviceInfo") + + ' does the users setup support surround sound? + maxAudioChannels = "2" ' jellyfin expects this as a string + ' in order of preference from left to right + ' aac will be highest priority unless surroundSoundCodec <> "aac" + audioCodecs = ["mp3", "vorbis", "opus", "flac", "alac", "ac4", "pcm", "wma", "wmapro"] + surroundSoundCodecs = ["aac", "eac3", "ac3", "vorbis", "dts"] + surroundSoundCodec = invalid if di.GetAudioOutputChannel() = "5.1 surround" maxAudioChannels = "6" - eightChannelCodecs = ["ac3", "eac3", "dts"] - for each eightChannelCodec in eightChannelCodecs - if di.CanDecodeAudio({ Codec: eightChannelCodec, ChCnt: 8 }).Result - maxAudioChannels = "8" + for each codec in surroundSoundCodecs + if di.CanDecodeAudio({ Codec: codec, ChCnt: 6 }).Result + surroundSoundCodec = codec + if di.CanDecodeAudio({ Codec: codec, ChCnt: 8 }).Result + maxAudioChannels = "8" + end if exit for end if end for end if - ' AVC / h264 + ' VIDEO CODECS + ' + ' AVC / h264 / MPEG4 AVC h264Profiles = ["main", "high"] h264Levels = ["4.1", "4.2"] - - for each container in transContainers + for each container in profileSupport for each profile in h264Profiles for each level in h264Levels if di.CanDecodeVideo({ Codec: "h264", Container: container, Profile: profile, Level: level }).Result - addH264Profile = true - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} + profileSupport[container] = updateProfileArray(profileSupport[container], "h264", profile, level) + end if + if di.CanDecodeVideo({ Codec: "mpeg4 avc", Container: container, Profile: profile, Level: level }).Result + profileSupport[container] = updateProfileArray(profileSupport[container], "mpeg4 avc", profile, level) + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, ",mpeg4 avc") = -1 + mp4VideoCodecs = mp4VideoCodecs + ",mpeg4 avc" + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, ",mpeg4 avc") = -1 + tsVideoCodecs = tsVideoCodecs + ",mpeg4 avc" + end if end if - if supportedVideoCodecs[container]["h264"] = invalid - supportedVideoCodecs[container]["h264"] = {} - end if - if supportedVideoCodecs[container]["h264"][profile] = invalid - supportedVideoCodecs[container]["h264"][profile] = [] - end if - supportedVideoCodecs[container]["h264"][profile].push(level) end if end for end for @@ -101,53 +117,80 @@ function getDeviceProfile() as object ' HEVC / h265 hevcProfiles = ["main", "main 10"] hevcLevels = ["4.1", "5.0", "5.1"] - - for each container in transContainers + addHevc = false + for each container in profileSupport for each profile in hevcProfiles for each level in hevcLevels if di.CanDecodeVideo({ Codec: "hevc", Container: container, Profile: profile, Level: level }).Result - addHevcProfile = true - ' hevc codec string - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} + addHevc = true + profileSupport[container] = updateProfileArray(profileSupport[container], "hevc", profile, level) + profileSupport[container] = updateProfileArray(profileSupport[container], "h265", profile, level) + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, "h265,") = -1 + mp4VideoCodecs = "h265," + mp4VideoCodecs + end if + if mp4VideoCodecs.Instr(0, "hevc,") = -1 + mp4VideoCodecs = "hevc," + mp4VideoCodecs + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, "h265,") = -1 + tsVideoCodecs = "h265," + tsVideoCodecs + end if + if tsVideoCodecs.Instr(0, "hevc,") = -1 + tsVideoCodecs = "hevc," + tsVideoCodecs + end if end if - if supportedVideoCodecs[container]["hevc"] = invalid - supportedVideoCodecs[container]["hevc"] = {} - end if - if supportedVideoCodecs[container]["hevc"][profile] = invalid - supportedVideoCodecs[container]["hevc"][profile] = [] - end if - supportedVideoCodecs[container]["hevc"][profile].push(level) - ' h265 codec string - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} - end if - if supportedVideoCodecs[container]["h265"] = invalid - supportedVideoCodecs[container]["h265"] = {} - end if - if supportedVideoCodecs[container]["h265"][profile] = invalid - supportedVideoCodecs[container]["h265"][profile] = [] - end if - supportedVideoCodecs[container]["h265"][profile].push(level) end if end for end for end for + ' VP9 + vp9Profiles = ["profile 0", "profile 2"] + addVp9 = false + for each container in profileSupport + for each profile in vp9Profiles + if di.CanDecodeAudio({ Codec: "vp9", Container: container, Profile: profile }).Result + addVp9 = true + profileSupport[container] = updateProfileArray(profileSupport[container], "vp9", profile) + + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, ",vp9") = -1 + mp4VideoCodecs = mp4VideoCodecs + ",vp9" + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, ",vp9") = -1 + tsVideoCodecs = tsVideoCodecs + ",vp9" + end if + end if + end if + end for + end for + ' MPEG2 - mpeg2Levels = ["main", "high"] + mpeg2Profiles = ["main", "high"] + addMpeg2 = false if playMpeg2 - for each container in transContainers - for each level in mpeg2Levels - if di.CanDecodeVideo({ Codec: "mpeg2", Container: container, Level: level }).Result - addMpeg2Profile = true - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} + for each container in profileSupport + for each profile in mpeg2Profiles + if di.CanDecodeVideo({ Codec: "mpeg2", Container: container, Profile: profile }).Result + addMpeg2 = true + profileSupport[container] = updateProfileArray(profileSupport[container], "mpeg2", profile) + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, ",mpeg2video") = -1 + mp4VideoCodecs = mp4VideoCodecs + ",mpeg2video" + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, ",mpeg2video") = -1 + tsVideoCodecs = tsVideoCodecs + ",mpeg2video" + end if end if - if supportedVideoCodecs[container]["mpeg2video"] = invalid - supportedVideoCodecs[container]["mpeg2video"] = [] - end if - supportedVideoCodecs[container]["mpeg2video"].push(level) end if end for end for @@ -156,153 +199,72 @@ function getDeviceProfile() as object ' AV1 av1Profiles = ["main", "main 10"] av1Levels = ["4.1", "5.0", "5.1"] + addAv1 = false if playAv1 - for each container in transContainers + for each container in profileSupport for each profile in av1Profiles for each level in av1Levels if di.CanDecodeVideo({ Codec: "av1", Container: container, Profile: profile, Level: level }).Result - addAv1Profile = true - ' av1 codec string - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} + addAv1 = true + profileSupport[container] = updateProfileArray(profileSupport[container], "av1", profile, level) + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, ",av1") = -1 + mp4VideoCodecs = mp4VideoCodecs + ",av1" + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, ",av1") = -1 + tsVideoCodecs = tsVideoCodecs + ",av1" + end if end if - if supportedVideoCodecs[container]["av1"] = invalid - supportedVideoCodecs[container]["av1"] = {} - end if - if supportedVideoCodecs[container]["av1"][profile] = invalid - supportedVideoCodecs[container]["av1"][profile] = [] - end if - supportedVideoCodecs[container]["av1"][profile].push(level) end if end for end for end for end if - ' VP9 - vp9Profiles = ["profile 0", "profile 2"] - - for each container in transContainers - for each profile in vp9Profiles - if di.CanDecodeVideo({ Codec: "vp9", Container: container, Profile: profile }).Result - addVp9Profile = true - ' vp9 codec string - if supportedVideoCodecs[container] = invalid - supportedVideoCodecs[container] = {} + ' AUDIO CODECS + for each container in profileSupport + for each codec in audioCodecs + if di.CanDecodeAudio({ Codec: codec, Container: container }).result + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, "," + codec) = -1 + mp4AudioCodecs = mp4AudioCodecs + "," + codec + end if + else if container = "ts" + ' check for codec string before adding it + if tsAudioCodecs.Instr(0, "," + codec) = -1 + tsAudioCodecs = tsAudioCodecs + "," + codec + end if end if - if supportedVideoCodecs[container]["vp9"] = invalid - supportedVideoCodecs[container]["vp9"] = [] - end if - supportedVideoCodecs[container]["vp9"].push(profile) end if end for end for - ' eac3 - for each container in transContainers - if di.CanDecodeAudio({ Codec: "eac3", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("eac3") - end if - end for - - - ' ac3 - for each container in transContainers - if di.CanDecodeAudio({ Codec: "ac3", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("ac3") - end if - end for - - ' dts - for each container in transContainers - if di.CanDecodeAudio({ Codec: "dts", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("dts") - end if - end for - - ' opus - for each container in transContainers - if di.CanDecodeAudio({ Codec: "opus", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("opus") - end if - end for - - ' flac - for each container in transContainers - if di.CanDecodeAudio({ Codec: "flac", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("flac") - end if - end for - - ' vorbis - for each container in transContainers - if di.CanDecodeAudio({ Codec: "vorbis", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("vorbis") - end if - end for - - ' aac - for each container in transContainers - if di.CanDecodeAudio({ Codec: "aac", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("aac") - end if - end for - - ' mp3 - for each container in transContainers - if di.CanDecodeAudio({ Codec: "mp3", Container: container }).result - if supportedAudioCodecs[container] = invalid - supportedAudioCodecs[container] = [] - end if - - supportedAudioCodecs[container].push("mp3") - end if - end for - + ' HDR SUPPORT + h264VideoRangeTypes = "SDR" hevcVideoRangeTypes = "SDR" vp9VideoRangeTypes = "SDR" av1VideoRangeTypes = "SDR" dp = di.GetDisplayProperties() - if dp.Hdr10 ' or dp.Hdr10Plus? + if dp.Hdr10 hevcVideoRangeTypes = hevcVideoRangeTypes + "|HDR10" vp9VideoRangeTypes = vp9VideoRangeTypes + "|HDR10" av1VideoRangeTypes = av1VideoRangeTypes + "|HDR10" end if + if dp.Hdr10Plus + av1VideoRangeTypes = av1VideoRangeTypes + "|HDR10+" + end if if dp.HLG hevcVideoRangeTypes = hevcVideoRangeTypes + "|HLG" vp9VideoRangeTypes = vp9VideoRangeTypes + "|HLG" av1VideoRangeTypes = av1VideoRangeTypes + "|HLG" end if if dp.DolbyVision + h264VideoRangeTypes = hevcVideoRangeTypes + "|DOVI" hevcVideoRangeTypes = hevcVideoRangeTypes + "|DOVI" 'vp9VideoRangeTypes = vp9VideoRangeTypes + ",DOVI" no evidence that vp9 can hold DOVI av1VideoRangeTypes = av1VideoRangeTypes + "|DOVI" @@ -318,18 +280,18 @@ function getDeviceProfile() as object "TranscodingProfiles": [], "ContainerProfiles": [], "CodecProfiles": [ - { - "Type": "VideoAudio", - "Codec": DirectPlayProfile[1].AudioCodec, ' Use supported MKV Audio list - "Conditions": [ - { - "Condition": "LessThanEqual", - "Property": "AudioChannels", - "Value": maxAudioChannels, - "IsRequired": false - } - ] - } + ' { + ' "Type": "VideoAudio", + ' "Codec": DirectPlayProfile[1].AudioCodec, ' Use supported MKV Audio list + ' "Conditions": [ + ' { + ' "Condition": "LessThanEqual", + ' "Property": "AudioChannels", + ' "Value": maxAudioChannels, + ' "IsRequired": false + ' } + ' ] + ' } ], "SubtitleProfiles": [ { @@ -352,141 +314,173 @@ function getDeviceProfile() as object } ' build TranscodingProfiles - ' create an audio profile for each audio codec supported by the mp4 container - for each supportedMp4AudioCodec in supportedAudioCodecs["mp4"] - ' streaming - deviceProfile.TranscodingProfiles.push({ - "Container": supportedMp4AudioCodec, + ' + for each audioCodec in mp4AudioCodecs.split(",") + streamingArray = { + "Container": audioCodec, "Type": "Audio", - "AudioCodec": supportedMp4AudioCodec, + "AudioCodec": audioCodec, "Context": "Streaming", "Protocol": "http", "MaxAudioChannels": maxAudioChannels - }) - ' static - deviceProfile.TranscodingProfiles.push({ - "Container": supportedMp4AudioCodec, + } + staticArray = { + "Container": audioCodec, "Type": "Audio", - "AudioCodec": supportedMp4AudioCodec, + "AudioCodec": audioCodec, "Context": "Static", "Protocol": "http", "MaxAudioChannels": maxAudioChannels - }) - end for - ' create a video profile for each container in transContainers - for each container in transContainers - audioCodecs = [] - videoCodecs = [] - for each codec in supportedAudioCodecs[container] - audioCodecs.push(codec) - end for - for each codec in supportedVideoCodecs[container] - videoCodecs.push(codec) - end for - containerArray = { - "Container": container, - "Context": "Static", - "Type": "Video", - "AudioCodec": audioCodecs.join(","), - "VideoCodec": videoCodecs.join(","), - "MaxAudioChannels": maxAudioChannels } - if container = "ts" - containerArray["Context"] = "Streaming" - containerArray["Protocol"] = "hls" - containerArray["MinSegments"] = "1" - containerArray["BreakOnNonKeyFrames"] = true - else if container = "mp4" - containerArray["Context"] = "Static" - containerArray["Protocol"] = "http" - end if - deviceProfile.TranscodingProfiles.push(containerArray) - end for + ' use ts container for aac + if audioCodec = "aac" + if di.CanDecodeAudio({ Codec: audioCodec, Container: "ts", ChCnt: maxAudioChannels.trim().ToInt() }).result + streamingArray["Container"] = "ts" + staticArray["Container"] = "ts" - ' build CodecProfiles - if addH264Profile - ' determine highest level supported - h264HighestLevel = 4.2 - h264HighestLevelSupported = 0.0 - for each container in transContainers - for each profile in hevcProfiles - for each level in supportedVideoCodecs[container]["h264"][profile] - levelFloat = level.ToFloat() - if levelFloat > h264HighestLevelSupported - h264HighestLevelSupported = levelFloat - end if - if h264HighestLevelSupported = h264HighestLevel then exit for - end for - if h264HighestLevelSupported = h264HighestLevel then exit for - end for - if h264HighestLevelSupported = h264HighestLevel then exit for - end for - - h264LevelString = "41" - if h264HighestLevelSupported = 4.2 - h264LevelString = "42" - end if - - - videoProfiles = [] - for each container in transContainers - if supportedVideoCodecs[container]["h264"] <> invalid - for each profile in supportedVideoCodecs[container]["h264"] - videoProfiles.push(profile) - end for - exit for + deviceProfile.TranscodingProfiles.push(streamingArray) + deviceProfile.TranscodingProfiles.push(staticArray) + end if + else + ' use audioCodec as container for everything else + if di.CanDecodeAudio({ Codec: audioCodec, Container: audioCodec, ChCnt: maxAudioChannels.trim().ToInt() }).result + deviceProfile.TranscodingProfiles.push(streamingArray) + deviceProfile.TranscodingProfiles.push(staticArray) end if - end for - - codecProfileArray = { - "Type": "Video", - "Codec": "h264", - "Conditions": [ - { - "Condition": "NotEquals", - "Property": "IsAnamorphic", - "Value": "true", - "IsRequired": false - }, - { - "Condition": "EqualsAny", - "Property": "VideoProfile", - "Value": videoProfiles.join("|"), - "IsRequired": false - }, - { - "Condition": "EqualsAny", - "Property": "VideoRangeType", - "Value": "SDR", - "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "VideoLevel", - "Value": h264LevelString, - "IsRequired": false - } - ] - } - bitRateArray = GetBitRateLimit("h264") - if bitRateArray.count() > 0 - codecProfileArray.Conditions.push(bitRateArray) end if - deviceProfile.CodecProfiles.push(codecProfileArray) + end for + + + tsArray = { + "Container": "ts", + "Context": "Streaming", + "Protocol": "hls", + "Type": "Video", + "AudioCodec": tsAudioCodecs, + "VideoCodec": tsVideoCodecs, + "MaxAudioChannels": maxAudioChannels, + "MinSegments": 1, + "BreakOnNonKeyFrames": false + } + mp4Array = { + "Container": "mp4", + "Context": "Streaming", + "Protocol": "hls", + "Type": "Video", + "AudioCodec": mp4AudioCodecs, + "VideoCodec": mp4VideoCodecs, + "MaxAudioChannels": maxAudioChannels, + "MinSegments": 1, + "BreakOnNonKeyFrames": false + } + + ' surround sound + ' move preferred surround sound codec to front of string + if maxAudioChannels.ToInt() > 2 + ' search codec strings for our preferred codec + tsCodecStringPosition = tsArray.AudioCodec.Instr(0, "," + surroundSoundCodec) + mp4CodecStringPosition = mp4Array.AudioCodec.Instr(0, "," + surroundSoundCodec) + + if tsCodecStringPosition <> -1 + ' ts supports our prefered codec + ' remove codec from string + tsArray.AudioCodec.Replace("," + surroundSoundCodec, "") + ' put codec in front of string + tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec + end if + if mp4CodecStringPosition <> -1 + ' mp4 supports our prefered codec + ' remove codec from string + mp4Array.AudioCodec.Replace("," + surroundSoundCodec, "") + ' put codec in front of string + mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec + end if end if - if addMpeg2Profile - mpeg2Levels = [] - for each container in transContainers - if supportedVideoCodecs[container] <> invalid - if supportedVideoCodecs[container]["mpeg2"] <> invalid - for each level in supportedVideoCodecs[container]["mpeg2"] - mpeg2Levels.push(level) - end for - if mpeg2Levels.count() > 0 then exit for + deviceProfile.TranscodingProfiles.push(tsArray) + deviceProfile.TranscodingProfiles.push(mp4Array) + + ' Build CodecProfiles + ' + ' H264 + h264Mp4LevelSupported = 0.0 + h264TsLevelSupported = 0.0 + h264AssProfiles = {} + h264LevelString = invalid + for each container in profileSupport + for each profile in profileSupport[container]["h264"] + h264AssProfiles.AddReplace(profile, true) + for each level in profileSupport[container]["h264"][profile] + levelFloat = level.ToFloat() + if container = "mp4" + if levelFloat > h264Mp4LevelSupported + h264Mp4LevelSupported = levelFloat + end if + else if container = "ts" + if levelFloat > h264TsLevelSupported + h264TsLevelSupported = levelFloat + end if end if - end if + end for + end for + end for + + h264LevelString = h264Mp4LevelSupported + if h264TsLevelSupported > h264Mp4LevelSupported + h264LevelString = h264TsLevelSupported + end if + ' convert to string + h264LevelString = h264LevelString.ToStr() + ' remove decimals + h264LevelString = removeDecimals(h264LevelString) + + + codecProfileArray = { + "Type": "Video", + "Codec": "h264", + "Conditions": [ + { + "Condition": "NotEquals", + "Property": "IsAnamorphic", + "Value": "true", + "IsRequired": false + }, + { + "Condition": "EqualsAny", + "Property": "VideoProfile", + "Value": h264AssProfiles.Keys().join("|"), + "IsRequired": false + }, + { + "Condition": "EqualsAny", + "Property": "VideoRangeType", + "Value": h264VideoRangeTypes, + "IsRequired": false + }, + { + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": h264LevelString, + "IsRequired": false + } + ] + } + bitRateArray = GetBitRateLimit("h264") + if bitRateArray.count() > 0 + codecProfileArray.Conditions.push(bitRateArray) + end if + deviceProfile.CodecProfiles.push(codecProfileArray) + + ' MPEG2 + if addMpeg2 + mpeg2Levels = [] + for each container in profileSupport + for each level in profileSupport[container]["mpeg2"] + if mpeg2Levels[level] = invalid + mpeg2Levels.push(level) + end if + end for end for codecProfileArray = { @@ -508,33 +502,35 @@ function getDeviceProfile() as object deviceProfile.CodecProfiles.push(codecProfileArray) end if - if addAv1Profile - ' determine highest level supported - av1HighestLevel = 5.1 - av1HighestLevelSupported = 0.0 - for each container in transContainers - for each profile in hevcProfiles - for each level in supportedVideoCodecs[container]["av1"][profile] + if addAv1 + av1Mp4LevelSupported = 0.0 + av1TsLevelSupported = 0.0 + av1AssProfiles = [] + av1HighestLevel = 0.0 + for each container in profileSupport + for each profile in profileSupport[container]["av1"] + av1AssProfiles.AddReplace(profile, true) + for each level in profileSupport[container]["av1"][profile] levelFloat = level.ToFloat() - if levelFloat > av1HighestLevelSupported - av1HighestLevelSupported = levelFloat + if container = "mp4" + if levelFloat > av1Mp4LevelSupported + av1Mp4LevelSupported = levelFloat + end if + else if container = "ts" + if levelFloat > av1TsLevelSupported + av1TsLevelSupported = levelFloat + end if end if - if av1HighestLevelSupported = av1HighestLevel then exit for end for - if av1HighestLevelSupported = av1HighestLevel then exit for end for - if av1HighestLevelSupported = av1HighestLevel then exit for end for - videoProfiles = [] - for each container in transContainers - if supportedVideoCodecs[container]["av1"] <> invalid - for each profile in supportedVideoCodecs[container]["av1"] - videoProfiles.push(profile) - end for - exit for - end if - end for + av1HighestLevel = av1Mp4LevelSupported + if av1TsLevelSupported > av1Mp4LevelSupported + av1HighestLevel = av1TsLevelSupported + end if + + codecProfileArray = { "Type": "Video", @@ -543,7 +539,7 @@ function getDeviceProfile() as object { "Condition": "EqualsAny", "Property": "VideoProfile", - "Value": videoProfiles.join("|"), + "Value": av1AssProfiles.Keys().join("|"), "IsRequired": false }, { @@ -555,7 +551,7 @@ function getDeviceProfile() as object { "Condition": "LessThanEqual", "Property": "VideoLevel", - "Value": (120 * av1HighestLevelSupported).ToStr(), + "Value": (120 * av1HighestLevel).ToStr(), "IsRequired": false } ] @@ -567,38 +563,38 @@ function getDeviceProfile() as object deviceProfile.CodecProfiles.push(codecProfileArray) end if - if addHevcProfile - ' determine highest level supported - hevcHighestLevel = 5.1 - hevcHighestLevelSupported = 0.0 - for each container in transContainers - for each profile in hevcProfiles - for each level in supportedVideoCodecs[container]["hevc"][profile] + if addHevc + hevcMp4LevelSupported = 0.0 + hevcTsLevelSupported = 0.0 + hevcAssProfiles = {} + hevcHighestLevel = 0.0 + for each container in profileSupport + for each profile in profileSupport[container]["hevc"] + hevcAssProfiles.AddReplace(profile, true) + for each level in profileSupport[container]["hevc"][profile] levelFloat = level.ToFloat() - if levelFloat > hevcHighestLevelSupported - hevcHighestLevelSupported = levelFloat + if container = "mp4" + if levelFloat > hevcMp4LevelSupported + hevcMp4LevelSupported = levelFloat + end if + else if container = "ts" + if levelFloat > hevcTsLevelSupported + hevcTsLevelSupported = levelFloat + end if end if - if hevcHighestLevelSupported = hevcHighestLevel then exit for end for - if hevcHighestLevelSupported = hevcHighestLevel then exit for end for - if hevcHighestLevelSupported = hevcHighestLevel then exit for end for - hevcLevelString = "120" - if hevcHighestLevelSupported = 5.1 - hevcLevelString = "153" + hevcHighestLevel = hevcMp4LevelSupported + if hevcTsLevelSupported > hevcMp4LevelSupported + hevcHighestLevel = hevcTsLevelSupported end if - videoProfiles = [] - for each container in transContainers - if supportedVideoCodecs[container]["hevc"] <> invalid - for each profile in supportedVideoCodecs[container]["hevc"] - videoProfiles.push(profile) - end for - exit for - end if - end for + hevcLevelString = "120" + if hevcHighestLevel = 5.1 + hevcLevelString = "153" + end if codecProfileArray = { "Type": "Video", @@ -613,7 +609,7 @@ function getDeviceProfile() as object { "Condition": "EqualsAny", "Property": "VideoProfile", - "Value": videoProfiles.join("|"), + "Value": profileSupport["ts"]["hevc"].Keys().join("|"), "IsRequired": false }, { @@ -630,6 +626,7 @@ function getDeviceProfile() as object } ] } + bitRateArray = GetBitRateLimit("h265") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) @@ -637,15 +634,14 @@ function getDeviceProfile() as object deviceProfile.CodecProfiles.push(codecProfileArray) end if - if addVp9Profile - videoProfiles = [] - for each container in transContainers - if supportedVideoCodecs[container]["vp9"] <> invalid - for each profile in supportedVideoCodecs[container]["vp9"] - videoProfiles.push(profile) - end for - exit for - end if + if addVp9 + vp9Profiles = [] + for each container in profileSupport + for each profile in profileSupport[container]["vp9"] + if vp9Profiles[profile] = invalid + vp9Profiles.push(profile) + end if + end for end for codecProfileArray = { @@ -654,8 +650,8 @@ function getDeviceProfile() as object "Conditions": [ { "Condition": "EqualsAny", - "Property": "VideoProfile", - "Value": videoProfiles.join("|"), + "Property": "VideoLevel", + "Value": vp9Profiles.join("|"), "IsRequired": false }, { @@ -666,6 +662,7 @@ function getDeviceProfile() as object } ] } + bitRateArray = GetBitRateLimit("vp9") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) @@ -834,3 +831,35 @@ function GetBitRateLimit(codec as string) as object end if return {} end function + +' Recieves and returns an assArray of supported profiles and levels for each video codec +function updateProfileArray(profileArray as object, videoCodec as string, videoProfile as string, profileLevel = "" as string) as object + ' validate params + if profileArray = invalid then return {} + if videoCodec = "" or videoProfile = "" then return profileArray + + if profileArray[videoCodec] = invalid + profileArray[videoCodec] = {} + end if + + if profileArray[videoCodec][videoProfile] = invalid + profileArray[videoCodec][videoProfile] = {} + end if + + ' add profileLevel if a value was provided + if profileLevel <> "" + if profileArray[videoCodec][videoProfile][profileLevel] = invalid + profileArray[videoCodec][videoProfile].AddReplace(profileLevel, true) + end if + end if + + ' profileSupport[container][codec][profile][level] + return profileArray +end function + +' Remove all decimals from a string +function removeDecimals(value as string) as string + r = CreateObject("roRegex", "\.", "") + value = r.ReplaceAll(value, "") + return value +end function From 3e26aabe017792873942eaa5dc91a3230fc13b13 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 6 Jun 2023 00:42:33 -0400 Subject: [PATCH 09/60] remove uneeded code + update surround sound codec logic --- source/utils/deviceCapabilities.brs | 39 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 3c15d366..94799243 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -64,7 +64,6 @@ function getDeviceProfile() as object ts: {} } - ' does the users setup support surround sound? maxAudioChannels = "2" ' jellyfin expects this as a string ' in order of preference from left to right @@ -229,15 +228,9 @@ function getDeviceProfile() as object for each codec in audioCodecs if di.CanDecodeAudio({ Codec: codec, Container: container }).result if container = "mp4" - ' check for codec string before adding it - if mp4VideoCodecs.Instr(0, "," + codec) = -1 - mp4AudioCodecs = mp4AudioCodecs + "," + codec - end if + mp4AudioCodecs = mp4AudioCodecs + "," + codec else if container = "ts" - ' check for codec string before adding it - if tsAudioCodecs.Instr(0, "," + codec) = -1 - tsAudioCodecs = tsAudioCodecs + "," + codec - end if + tsAudioCodecs = tsAudioCodecs + "," + codec end if end if end for @@ -381,21 +374,35 @@ function getDeviceProfile() as object ' search codec strings for our preferred codec tsCodecStringPosition = tsArray.AudioCodec.Instr(0, "," + surroundSoundCodec) mp4CodecStringPosition = mp4Array.AudioCodec.Instr(0, "," + surroundSoundCodec) - + ' ts if tsCodecStringPosition <> -1 - ' ts supports our prefered codec ' remove codec from string tsArray.AudioCodec.Replace("," + surroundSoundCodec, "") - ' put codec in front of string - tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec end if + ' check beginning of array before adding to it + if tsArray.AudioCodec.Instr(0, surroundSoundCodec) = -1 + ' put codec in front of string + if tsArray.AudioCodec <> "" + tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec + else + tsArray.AudioCodec = surroundSoundCodec + end if + end if + ' mp4 if mp4CodecStringPosition <> -1 - ' mp4 supports our prefered codec ' remove codec from string mp4Array.AudioCodec.Replace("," + surroundSoundCodec, "") - ' put codec in front of string - mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec end if + ' check beginning of array before adding to it + if mp4Array.AudioCodec.Instr(0, surroundSoundCodec) = -1 + ' put codec in front of string + if mp4Array.AudioCodec <> "" + mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec + else + mp4Array.AudioCodec = surroundSoundCodec + end if + end if + end if deviceProfile.TranscodingProfiles.push(tsArray) From 9acf98fba4b59ba8bf7d2d3755c268f154a9332f Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 6 Jun 2023 21:53:26 -0400 Subject: [PATCH 10/60] remove empty lines --- source/utils/deviceCapabilities.brs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 94799243..2185a81c 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -344,7 +344,6 @@ function getDeviceProfile() as object end if end for - tsArray = { "Container": "ts", "Context": "Streaming", @@ -382,10 +381,10 @@ function getDeviceProfile() as object ' check beginning of array before adding to it if tsArray.AudioCodec.Instr(0, surroundSoundCodec) = -1 ' put codec in front of string - if tsArray.AudioCodec <> "" - tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec - else + if tsArray.AudioCodec = "" tsArray.AudioCodec = surroundSoundCodec + else + tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec end if end if ' mp4 @@ -396,10 +395,10 @@ function getDeviceProfile() as object ' check beginning of array before adding to it if mp4Array.AudioCodec.Instr(0, surroundSoundCodec) = -1 ' put codec in front of string - if mp4Array.AudioCodec <> "" - mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec - else + if mp4Array.AudioCodec = "" mp4Array.AudioCodec = surroundSoundCodec + else + mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec end if end if @@ -442,7 +441,6 @@ function getDeviceProfile() as object ' remove decimals h264LevelString = removeDecimals(h264LevelString) - codecProfileArray = { "Type": "Video", "Codec": "h264", @@ -537,8 +535,6 @@ function getDeviceProfile() as object av1HighestLevel = av1TsLevelSupported end if - - codecProfileArray = { "Type": "Video", "Codec": "av1", @@ -680,7 +676,6 @@ function getDeviceProfile() as object return deviceProfile end function - function GetDirectPlayProfiles() as object di = CreateObject("roDeviceInfo") ' all possible containers From 67424ae7b1360139196dfa66bdb08644ed49644f Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 6 Jun 2023 22:14:17 -0400 Subject: [PATCH 11/60] update possible direct play codecs --- source/utils/deviceCapabilities.brs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 2185a81c..1483b53e 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -706,9 +706,8 @@ function GetDirectPlayProfiles() as object } } ' all possible codecs - videoCodecs = ["h264", "vp8", "hevc", "vp9"] - audioCodecs = ["mp3", "pcm", "lpcm", "wav", "ac3", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] - + videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1"] + audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] ' respect user settings if m.global.session.user.settings["playback.mpeg4"] videoCodecs.push("mpeg4") From 8d8b2e2f11f64e7d78e05aff7c1ee9979f7b44d5 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 9 Jun 2023 11:28:10 -0400 Subject: [PATCH 12/60] add debugging info when attempting to play a video --- components/ItemGrid/LoadVideoContentTask.brs | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index 1fcbf302..5c26b16a 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -75,6 +75,38 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s return end if + print "meta =", meta + print "meta.json =", meta.json + print "meta.json.MediaSources[0] =", meta.json.MediaSources[0] + print "meta.json.MediaSources[0].MediaStreams[0] =", meta.json.MediaSources[0].MediaStreams[0] + print "meta.json.MediaStreams[0] =", meta.json.MediaStreams[0] + devinfo = CreateObject("roDeviceInfo") + myCodec = meta.json.MediaSources[0].MediaStreams[0].Codec + print "myCodec =", myCodec + myContainer = meta.json.MediaSources[0].Container + print "myContainer =", myContainer + myProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) + print "myProfile =", myProfile + myLevel = meta.json.MediaSources[0].MediaStreams[0].Level + if myLevel.ToStr().Len() = 2 + myLevel = myLevel / 10 + end if + myLevel = myLevel.ToStr() + print "myLevel =", myLevel + print "type(myLevel) =", type(myLevel) + + if isValid(myProfile) and isValid(myLevel) + canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer, Profile: myProfile, Level: myLevel }) + else if isValid(myProfile) + canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer, Profile: myProfile }) + else + canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer }) + end if + print "canDecode =", canDecode + print "canDecode.Result =", canDecode.Result + print "canDecode.Updated =", canDecode.Updated + print "canDecode.[canDecode.Updated] =", canDecode.[canDecode.Updated] + videotype = LCase(meta.type) if videotype = "episode" or videotype = "series" @@ -189,6 +221,7 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s video.content = authorize_request(video.content) end if + print "video.directPlaySupported =", video.directPlaySupported end sub sub addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external) From db3d47ef25c428244e623c8468455dcff8462b3a Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 9 Jun 2023 23:26:47 -0400 Subject: [PATCH 13/60] add more debug info about selected video --- components/ItemGrid/LoadVideoContentTask.brs | 87 +++++++++++++++----- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index 5c26b16a..def0f8ae 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -79,33 +79,82 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s print "meta.json =", meta.json print "meta.json.MediaSources[0] =", meta.json.MediaSources[0] print "meta.json.MediaSources[0].MediaStreams[0] =", meta.json.MediaSources[0].MediaStreams[0] + print "meta.json.MediaSources[0].MediaStreams[1] =", meta.json.MediaSources[0].MediaStreams[1] print "meta.json.MediaStreams[0] =", meta.json.MediaStreams[0] devinfo = CreateObject("roDeviceInfo") - myCodec = meta.json.MediaSources[0].MediaStreams[0].Codec - print "myCodec =", myCodec + + myVideoCodec = meta.json.MediaSources[0].MediaStreams[0].Codec + print "myVideoCodec =", myVideoCodec myContainer = meta.json.MediaSources[0].Container print "myContainer =", myContainer - myProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) - print "myProfile =", myProfile - myLevel = meta.json.MediaSources[0].MediaStreams[0].Level - if myLevel.ToStr().Len() = 2 - myLevel = myLevel / 10 + myVidProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) + print "myVidProfile =", myVidProfile + myVidLevel = meta.json.MediaSources[0].MediaStreams[0].Level + if myVidLevel.ToStr().Len() = 2 + myVidLevel = myVidLevel / 10 end if - myLevel = myLevel.ToStr() - print "myLevel =", myLevel - print "type(myLevel) =", type(myLevel) + myVidLevel = myVidLevel.ToStr() + print "myVidLevel =", myVidLevel + print "type(myVidLevel) =", type(myVidLevel) - if isValid(myProfile) and isValid(myLevel) - canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer, Profile: myProfile, Level: myLevel }) - else if isValid(myProfile) - canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer, Profile: myProfile }) + if isValid(myVidProfile) and isValid(myVidLevel) + canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer, Profile: myVidProfile, Level: myVidLevel }) + else if isValid(myVidProfile) + canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer, Profile: myVidProfile }) else - canDecode = devinfo.CanDecodeVideo({ Codec: myCodec, Container: myContainer }) + canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer }) + end if + print "canDecodeVid =", canDecodeVid + print "canDecodeVid.Result =", canDecodeVid.Result + if canDecodeVid.Updated <> invalid + print "canDecodeVid.Updated =", canDecodeVid.Updated + print "canDecodeVid.[canDecodeVid.Updated] =", canDecodeVid.[canDecodeVid.Updated] + end if + + myAudioCodec = meta.json.MediaSources[0].MediaStreams[1].Codec + print "myAudioCodec =", myAudioCodec + myAudioProfile = Lcase(meta.json.MediaSources[0].MediaStreams[1].Profile) + if myAudioProfile <> invalid and myAudioProfile <> "" + if myAudioCodec = "aac" + if myAudioProfile = "lc" + myAudioProfile = "mp2 lc" + else if myAudioProfile = "he" + myAudioProfile = "mp4 he" + end if + end if + end if + print "myAudioProfile =", myAudioProfile + myAudioLevel = meta.json.MediaSources[0].MediaStreams[1].Level + if myAudioLevel.ToStr().Len() = 2 + myAudioLevel = myAudioLevel / 10 + end if + myAudioLevel = myAudioLevel.ToStr() + print "myAudioLevel =", myAudioLevel + myAudioChannels = meta.json.MediaSources[0].MediaStreams[1].Channels + print "myAudioChannels =", myAudioChannels + myAudioBitrate = meta.json.MediaSources[0].MediaStreams[1].BitRate + myAudioBitrate = myAudioBitrate / 1000 ' convert from bits per second to Kbit/sec + myAudioBitrate = StrToI(myAudioBitrate.ToStr()) + print "myAudioBitrate =", myAudioBitrate + print "type(myAudioBitrate) =", type(myAudioBitrate) + + if isValid(myAudioProfile) and myAudioLevel <> "0" + print "audio profile and level provided" + canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels, Profile: myAudioProfile, Level: myAudioLevel }) + else if isValid(myAudioProfile) + print "audio profile provided" + canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels, Profile: myAudioProfile }) + else + print "neither audio profile nor level provided" + canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels }) + end if + + print "canDecodeAud =", canDecodeAud + print "canDecodeAud.Result =", canDecodeAud.Result + if canDecodeAud.Updated <> invalid + print "canDecodeAud.Updated =", canDecodeAud.Updated + print "canDecodeAud.[canDecodeAud.Updated] =", canDecodeAud.[canDecodeAud.Updated] end if - print "canDecode =", canDecode - print "canDecode.Result =", canDecode.Result - print "canDecode.Updated =", canDecode.Updated - print "canDecode.[canDecode.Updated] =", canDecode.[canDecode.Updated] videotype = LCase(meta.type) From 4969cc7a1303f6979c9aae4bec0411d839c85b4b Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 23 Jun 2023 10:23:21 -0400 Subject: [PATCH 14/60] remove aac from list of surround sound codecs --- source/utils/deviceCapabilities.brs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 1483b53e..f7ca6f86 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -67,9 +67,8 @@ function getDeviceProfile() as object ' does the users setup support surround sound? maxAudioChannels = "2" ' jellyfin expects this as a string ' in order of preference from left to right - ' aac will be highest priority unless surroundSoundCodec <> "aac" audioCodecs = ["mp3", "vorbis", "opus", "flac", "alac", "ac4", "pcm", "wma", "wmapro"] - surroundSoundCodecs = ["aac", "eac3", "ac3", "vorbis", "dts"] + surroundSoundCodecs = ["eac3", "ac3", "vorbis", "dts"] surroundSoundCodec = invalid if di.GetAudioOutputChannel() = "5.1 surround" maxAudioChannels = "6" From def690096f971d153e717f3a47fe24189be95865 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 30 Jun 2023 15:02:35 -0400 Subject: [PATCH 15/60] add CODEOWNERS file --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..8b344eb9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @jellyfin/roku \ No newline at end of file From dbfe4428aaa5bcbcfad17dce07b7f615c93df5aa Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Mon, 3 Jul 2023 18:48:31 -0400 Subject: [PATCH 16/60] Add static analysis to build job --- .github/workflows/build-prod.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index 2501adad..dee2ba34 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -72,6 +72,17 @@ jobs: run: npm run ropm - name: Build app for production run: npm run build-prod + - name: Use Java 17 + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + - name: Download the Static Channel Analysis CLI + run: | + curl -sSL "https://devtools.web.roku.com/static-channel-analysis/sca-cmd.zip" -o sca-cmd.zip + unzip sca-cmd.zip + - name: Run Analysis + run: ./sca-cmd/bin/sca-cmd ${{ github.workspace }}/build/staging --exit error - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 with: name: Jellyfin-Roku-v${{ env.newManVersion }}-${{ github.sha }} From 46d7333b6263dce4dde50ecce0a50fb9c8023b89 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 17 Aug 2023 21:51:59 -0400 Subject: [PATCH 17/60] fix bug with mpeg2 levels --- source/utils/deviceCapabilities.brs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index f7ca6f86..cb5bd224 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -170,14 +170,16 @@ function getDeviceProfile() as object end for ' MPEG2 - mpeg2Profiles = ["main", "high"] + ' mpeg2 uses levels with no profiles. see https://developer.roku.com/en-ca/docs/references/brightscript/interfaces/ifdeviceinfo.md#candecodevideovideo_format-as-object-as-object + ' NOTE: the mpeg2 levels are being saved in the profileSupport array as if they were profiles + mpeg2Levels = ["main", "high"] addMpeg2 = false if playMpeg2 for each container in profileSupport - for each profile in mpeg2Profiles - if di.CanDecodeVideo({ Codec: "mpeg2", Container: container, Profile: profile }).Result + for each level in mpeg2Levels + if di.CanDecodeVideo({ Codec: "mpeg2", Container: container, Level: level }).Result addMpeg2 = true - profileSupport[container] = updateProfileArray(profileSupport[container], "mpeg2", profile) + profileSupport[container] = updateProfileArray(profileSupport[container], "mpeg2", level) if container = "mp4" ' check for codec string before adding it if mp4VideoCodecs.Instr(0, ",mpeg2video") = -1 @@ -477,6 +479,7 @@ function getDeviceProfile() as object deviceProfile.CodecProfiles.push(codecProfileArray) ' MPEG2 + ' NOTE: the mpeg2 levels are being saved in the profileSupport array as if they were profiles if addMpeg2 mpeg2Levels = [] for each container in profileSupport From 1ac8bfc16efbb655d297f6806973e3c62c63b48d Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 22 Aug 2023 22:32:41 -0400 Subject: [PATCH 18/60] Create seperate workflow for roku analysis --- .github/workflows/build-prod.yml | 11 --------- .github/workflows/roku-analysis.yml | 38 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/roku-analysis.yml diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index dee2ba34..2501adad 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -72,17 +72,6 @@ jobs: run: npm run ropm - name: Build app for production run: npm run build-prod - - name: Use Java 17 - uses: actions/setup-java@v3 - with: - distribution: "temurin" - java-version: "17" - - name: Download the Static Channel Analysis CLI - run: | - curl -sSL "https://devtools.web.roku.com/static-channel-analysis/sca-cmd.zip" -o sca-cmd.zip - unzip sca-cmd.zip - - name: Run Analysis - run: ./sca-cmd/bin/sca-cmd ${{ github.workspace }}/build/staging --exit error - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 with: name: Jellyfin-Roku-v${{ env.newManVersion }}-${{ github.sha }} diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml new file mode 100644 index 00000000..1a3b05ac --- /dev/null +++ b/.github/workflows/roku-analysis.yml @@ -0,0 +1,38 @@ +name: roku-analysis + +on: + pull_request: + +env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + +jobs: + static: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3 + - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 + with: + node-version: "lts/*" + cache: "npm" + - name: NPM install + run: npm ci + - name: Install roku module dependencies + run: npm run ropm + - name: Build dev app + if: env.BRANCH_NAME != 'master' + run: npm run build + - name: Build app for production + if: env.BRANCH_NAME == 'master' + run: npm run build-prod + - name: Use Java 17 + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + - name: Download the Static Channel Analysis CLI + run: | + curl -sSL "https://devtools.web.roku.com/static-channel-analysis/sca-cmd.zip" -o sca-cmd.zip + unzip sca-cmd.zip + - name: Run Roku Static Analysis + run: ./sca-cmd/bin/sca-cmd ${{ github.workspace }}/build/staging --exit error From b923575d0db00bab99f22389532622aacf4a60dc Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Tue, 22 Aug 2023 22:38:45 -0400 Subject: [PATCH 19/60] Run roku-analysis workflow on push --- .github/workflows/roku-analysis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml index 1a3b05ac..e046b220 100644 --- a/.github/workflows/roku-analysis.yml +++ b/.github/workflows/roku-analysis.yml @@ -2,6 +2,7 @@ name: roku-analysis on: pull_request: + push: env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} From 05a04062fb9b6265bda41bb1b3f039d5099eb2c1 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 15:06:33 -0400 Subject: [PATCH 20/60] remove aac from transcoding profile when multichannel audio support is detected --- source/utils/deviceCapabilities.brs | 78 ++++++++++++++++------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index cb5bd224..a4a414e5 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -68,7 +68,7 @@ function getDeviceProfile() as object maxAudioChannels = "2" ' jellyfin expects this as a string ' in order of preference from left to right audioCodecs = ["mp3", "vorbis", "opus", "flac", "alac", "ac4", "pcm", "wma", "wmapro"] - surroundSoundCodecs = ["eac3", "ac3", "vorbis", "dts"] + surroundSoundCodecs = ["eac3", "ac3", "dts", "vorbis"] surroundSoundCodec = invalid if di.GetAudioOutputChannel() = "5.1 surround" maxAudioChannels = "6" @@ -309,41 +309,49 @@ function getDeviceProfile() as object ' build TranscodingProfiles ' - for each audioCodec in mp4AudioCodecs.split(",") - streamingArray = { - "Container": audioCodec, - "Type": "Audio", - "AudioCodec": audioCodec, - "Context": "Streaming", - "Protocol": "http", - "MaxAudioChannels": maxAudioChannels - } - staticArray = { - "Container": audioCodec, - "Type": "Audio", - "AudioCodec": audioCodec, - "Context": "Static", - "Protocol": "http", - "MaxAudioChannels": maxAudioChannels - } + ' always add mp3 to TranscodingProfile for music + deviceProfile.TranscodingProfiles.push({ + "Container": "mp3", + "Type": "Audio", + "AudioCodec": "mp3", + "Context": "Streaming", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) + deviceProfile.TranscodingProfiles.push({ + "Container": "mp3", + "Type": "Audio", + "AudioCodec": "mp3", + "Context": "Static", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) - ' use ts container for aac - if audioCodec = "aac" - if di.CanDecodeAudio({ Codec: audioCodec, Container: "ts", ChCnt: maxAudioChannels.trim().ToInt() }).result - streamingArray["Container"] = "ts" - staticArray["Container"] = "ts" + audioCodec = invalid + if surroundSoundCodec = invalid + ' use aac for all 2 channel transcoding + audioCodec = "aac" + else + ' use best available codec for all multichannel transcoding + audioCodec = surroundSoundCodec + end if - deviceProfile.TranscodingProfiles.push(streamingArray) - deviceProfile.TranscodingProfiles.push(staticArray) - end if - else - ' use audioCodec as container for everything else - if di.CanDecodeAudio({ Codec: audioCodec, Container: audioCodec, ChCnt: maxAudioChannels.trim().ToInt() }).result - deviceProfile.TranscodingProfiles.push(streamingArray) - deviceProfile.TranscodingProfiles.push(staticArray) - end if - end if - end for + deviceProfile.TranscodingProfiles.push({ + "Container": audioCodec, + "Type": "Audio", + "AudioCodec": audioCodec, + "Context": "Streaming", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) + deviceProfile.TranscodingProfiles.push({ + "Container": audioCodec, + "Type": "Audio", + "AudioCodec": audioCodec, + "Context": "Static", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) tsArray = { "Container": "ts", @@ -370,7 +378,7 @@ function getDeviceProfile() as object ' surround sound ' move preferred surround sound codec to front of string - if maxAudioChannels.ToInt() > 2 + if surroundSoundCodec <> invalid ' search codec strings for our preferred codec tsCodecStringPosition = tsArray.AudioCodec.Instr(0, "," + surroundSoundCodec) mp4CodecStringPosition = mp4Array.AudioCodec.Instr(0, "," + surroundSoundCodec) From 9f2fe6e8fc7744b562d507930f38f0e73186cf9a Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 19:30:50 -0400 Subject: [PATCH 21/60] save and parse videoMode to global device object --- source/utils/globals.brs | 58 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/source/utils/globals.brs b/source/utils/globals.brs index f5f64be8..3c629102 100644 --- a/source/utils/globals.brs +++ b/source/utils/globals.brs @@ -43,6 +43,57 @@ sub SaveDeviceToGlobal() ' remove special characters regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "") filteredFriendly = regex.ReplaceAll(deviceInfo.getFriendlyName(), "") + ' determine max playback resolution + ' https://developer.roku.com/en-ca/docs/references/brightscript/interfaces/ifdeviceinfo.md#getvideomode-as-string + videoMode = deviceInfo.GetVideoMode() + iPos = Instr(1, videoMode, "i") + pPos = Instr(1, videoMode, "p") + videoHeight = invalid + videoWidth = invalid + refreshRate = invalid + bitDepth = 8 + extraData = invalid + heightToWidth = { + "480": "720", + "576": "720", + "720": "1280", + "1080": "1920", + "2160": "3840", + "4320": "7680" + + } + if iPos > 0 and pPos = 0 + ' videMode = 000i + videoHeight = mid(videoMode, 1, iPos - 1) + ' save refresh rate + if Len(videoMode) > iPos + refreshRate = mid(videoMode, iPos + 1, 2) + end if + ' save whats left of string + if Len(videoMode) > iPos + 2 + extraData = mid(videoMode, iPos + 3) + end if + else if iPos = 0 and pPos > 0 + ' videMode = 000p + videoHeight = mid(videoMode, 1, pPos - 1) + ' save refresh rate + if Len(videoMode) > pPos + refreshRate = mid(videoMode, pPos + 1, 2) + end if + ' save whats left of string + if Len(videoMode) > pPos + 2 + extraData = mid(videoMode, pPos + 3) + end if + else + 'i and p not present in videoMode + print "ERROR parsing deviceInfo.GetVideoMode()" + end if + videoWidth = heightToWidth[videoHeight] + if videoHeight = "2160" and extraData = "b10" + bitDepth = 10 + else if videoHeight = "4320" + bitDepth = 12 + end if m.global.addFields({ device: { id: deviceInfo.getChannelClientID(), @@ -58,7 +109,12 @@ sub SaveDeviceToGlobal() hasVoiceRemote: deviceInfo.HasFeature("voice_remote"), displayType: deviceInfo.GetDisplayType(), - displayMode: deviceInfo.GetDisplayMode() + displayMode: deviceInfo.GetDisplayMode(), + videoMode: videoMode, + videoHeight: videoHeight, + videoWidth: videoWidth, + videoRefresh: Val(refreshRate), + videoBitDepth: Val(bitDepth) } }) end sub From 7ac0214d64e8fc738d5ffbc864004b666d6c3845 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 19:37:44 -0400 Subject: [PATCH 22/60] set max video width and height in codec profiles --- source/utils/deviceCapabilities.brs | 68 +++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index a4a414e5..27314260 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -472,6 +472,18 @@ function getDeviceProfile() as object "Value": h264VideoRangeTypes, "IsRequired": false }, + { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": m.global.device.videoWidth, + "IsRequired": true + }, + { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": m.global.device.videoHeight, + "IsRequired": true + }, { "Condition": "LessThanEqual", "Property": "VideoLevel", @@ -507,7 +519,19 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": mpeg2Levels.join("|"), "IsRequired": false - } + }, + { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": m.global.device.videoWidth, + "IsRequired": true + }, + { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": m.global.device.videoHeight, + "IsRequired": true + }, ] } bitRateArray = GetBitRateLimit("mpeg2") @@ -566,7 +590,19 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": (120 * av1HighestLevel).ToStr(), "IsRequired": false - } + }, + { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": m.global.device.videoWidth, + "IsRequired": true + }, + { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": m.global.device.videoHeight, + "IsRequired": true + }, ] } bitRateArray = GetBitRateLimit("av1") @@ -636,7 +672,19 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": hevcLevelString, "IsRequired": false - } + }, + { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": m.global.device.videoWidth, + "IsRequired": true + }, + { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": m.global.device.videoHeight, + "IsRequired": true + }, ] } @@ -672,7 +720,19 @@ function getDeviceProfile() as object "Property": "VideoRangeType", "Value": vp9VideoRangeTypes, "IsRequired": false - } + }, + { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": m.global.device.videoWidth, + "IsRequired": true + }, + { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": m.global.device.videoHeight, + "IsRequired": true + }, ] } From deebbe393f89f9fe0181bbfbfcc16deeb5b12aab Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 20:02:35 -0400 Subject: [PATCH 23/60] bugfix for h264 VideoRangeType --- source/utils/deviceCapabilities.brs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 27314260..e0d4280d 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -258,7 +258,7 @@ function getDeviceProfile() as object av1VideoRangeTypes = av1VideoRangeTypes + "|HLG" end if if dp.DolbyVision - h264VideoRangeTypes = hevcVideoRangeTypes + "|DOVI" + h264VideoRangeTypes = h264VideoRangeTypes + "|DOVI" hevcVideoRangeTypes = hevcVideoRangeTypes + "|DOVI" 'vp9VideoRangeTypes = vp9VideoRangeTypes + ",DOVI" no evidence that vp9 can hold DOVI av1VideoRangeTypes = av1VideoRangeTypes + "|DOVI" From b3d0409d9de2f9cdf6178dc4f0f03778ce78afc0 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 21:42:49 -0400 Subject: [PATCH 24/60] fix type mismatch --- source/utils/globals.brs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/utils/globals.brs b/source/utils/globals.brs index 3c629102..f53a6852 100644 --- a/source/utils/globals.brs +++ b/source/utils/globals.brs @@ -50,7 +50,7 @@ sub SaveDeviceToGlobal() pPos = Instr(1, videoMode, "p") videoHeight = invalid videoWidth = invalid - refreshRate = invalid + refreshRate = "0" bitDepth = 8 extraData = invalid heightToWidth = { @@ -113,8 +113,8 @@ sub SaveDeviceToGlobal() videoMode: videoMode, videoHeight: videoHeight, videoWidth: videoWidth, - videoRefresh: Val(refreshRate), - videoBitDepth: Val(bitDepth) + videoRefresh: StrToI(refreshRate), + videoBitDepth: bitDepth } }) end sub From c03a1b1feab3822ef1923027422abd9e02807ecd Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 21:47:18 -0400 Subject: [PATCH 25/60] fix bug in debugging code --- components/ItemGrid/LoadVideoContentTask.brs | 33 ++++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index def0f8ae..54ee24c8 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -113,23 +113,30 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s myAudioCodec = meta.json.MediaSources[0].MediaStreams[1].Codec print "myAudioCodec =", myAudioCodec - myAudioProfile = Lcase(meta.json.MediaSources[0].MediaStreams[1].Profile) - if myAudioProfile <> invalid and myAudioProfile <> "" - if myAudioCodec = "aac" - if myAudioProfile = "lc" - myAudioProfile = "mp2 lc" - else if myAudioProfile = "he" - myAudioProfile = "mp4 he" + + myAudioProfile = invalid + if isValid(meta.json.MediaSources[0].MediaStreams[1].Profile) + myAudioProfile = Lcase(meta.json.MediaSources[0].MediaStreams[1].Profile) + if myAudioProfile <> invalid and myAudioProfile <> "" + if myAudioCodec = "aac" + if myAudioProfile = "lc" + myAudioProfile = "mp2 lc" + else if myAudioProfile = "he" + myAudioProfile = "mp4 he" + end if end if end if + print "myAudioProfile =", myAudioProfile end if - print "myAudioProfile =", myAudioProfile - myAudioLevel = meta.json.MediaSources[0].MediaStreams[1].Level - if myAudioLevel.ToStr().Len() = 2 - myAudioLevel = myAudioLevel / 10 + myAudioLevel = invalid + if isValid(meta.json.MediaSources[0].MediaStreams[1].Level) + myAudioLevel = meta.json.MediaSources[0].MediaStreams[1].Level + if myAudioLevel.ToStr().Len() = 2 + myAudioLevel = myAudioLevel / 10 + end if + myAudioLevel = myAudioLevel.ToStr() + print "myAudioLevel =", myAudioLevel end if - myAudioLevel = myAudioLevel.ToStr() - print "myAudioLevel =", myAudioLevel myAudioChannels = meta.json.MediaSources[0].MediaStreams[1].Channels print "myAudioChannels =", myAudioChannels myAudioBitrate = meta.json.MediaSources[0].MediaStreams[1].BitRate From 8dd2c55df27e3015212a02d3acbfb16d7e9fe6cb Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Wed, 30 Aug 2023 22:51:47 -0400 Subject: [PATCH 26/60] make codec user settings override what device reports --- source/utils/deviceCapabilities.brs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index e0d4280d..7c1821e9 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -776,16 +776,8 @@ function GetDirectPlayProfiles() as object } } ' all possible codecs - videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1"] + videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1", "mpeg4", "mpeg2"] audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] - ' respect user settings - if m.global.session.user.settings["playback.mpeg4"] - videoCodecs.push("mpeg4") - end if - if m.global.session.user.settings["playback.mpeg2"] - videoCodecs.push("mpeg2") - end if - ' check video codecs for each container for each container in supportedCodecs for each videoCodec in videoCodecs @@ -802,6 +794,21 @@ function GetDirectPlayProfiles() as object end if end for end for + ' user settings override what the device thinks + if m.global.session.user.settings["playback.mpeg4"] + for each container in supportedCodecs + if supportedCodecs[container]["video"]["mpeg4"] = invalid + supportedCodecs[container]["video"].push("mpeg4") + end if + end for + end if + if m.global.session.user.settings["playback.mpeg2"] + for each container in supportedCodecs + if supportedCodecs[container]["video"]["mpeg2video"] = invalid + supportedCodecs[container]["video"].push("mpeg2video") + end if + end for + end if ' check audio codecs for each container for each container in supportedCodecs From f572b8a4e2e6390ccd9fb74d592c618af815472d Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 31 Aug 2023 16:02:39 -0700 Subject: [PATCH 27/60] bugfix + create helper function for searching array contents --- source/utils/deviceCapabilities.brs | 7 +++++-- source/utils/misc.brs | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 7c1821e9..e4340e5f 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -1,3 +1,5 @@ +import "pkg:/source/utils/misc.brs" + 'Device Capabilities for Roku. 'This will likely need further tweaking function getDeviceCapabilities() as object @@ -794,17 +796,18 @@ function GetDirectPlayProfiles() as object end if end for end for + ' user settings override what the device thinks if m.global.session.user.settings["playback.mpeg4"] for each container in supportedCodecs - if supportedCodecs[container]["video"]["mpeg4"] = invalid + if not arrayHasValue(supportedCodecs[container]["video"], "mpeg4") supportedCodecs[container]["video"].push("mpeg4") end if end for end if if m.global.session.user.settings["playback.mpeg2"] for each container in supportedCodecs - if supportedCodecs[container]["video"]["mpeg2video"] = invalid + if not arrayHasValue(supportedCodecs[container]["video"], "mpeg2video") supportedCodecs[container]["video"].push("mpeg2video") end if end for diff --git a/source/utils/misc.brs b/source/utils/misc.brs index 17839308..7865c53f 100644 --- a/source/utils/misc.brs +++ b/source/utils/misc.brs @@ -367,3 +367,13 @@ sub stopLoadingSpinner() m.scene.dialog.close = true end if end sub + +' Check if a specific value is inside of an array +function arrayHasValue(arr as object, value as dynamic) as boolean + for each entry in arr + if entry = value + return true + end if + end for + return false +end function From 45ad5bef96db045eb7716bd9948a708a4df03e9c Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 31 Aug 2023 17:02:39 -0700 Subject: [PATCH 28/60] bugfix for debug code --- components/ItemGrid/LoadVideoContentTask.brs | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index 54ee24c8..532f8250 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -87,15 +87,23 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s print "myVideoCodec =", myVideoCodec myContainer = meta.json.MediaSources[0].Container print "myContainer =", myContainer - myVidProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) - print "myVidProfile =", myVidProfile - myVidLevel = meta.json.MediaSources[0].MediaStreams[0].Level - if myVidLevel.ToStr().Len() = 2 - myVidLevel = myVidLevel / 10 + + myVidProfile = invalid + if isValid(meta.json.MediaSources[0].MediaStreams[0].Profile) + myVidProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) + print "myVidProfile =", myVidProfile + end if + + myVidLevel = invalid + if isValid(meta.json.MediaSources[0].MediaStreams[0].Level) + myVidLevel = meta.json.MediaSources[0].MediaStreams[0].Level + if myVidLevel.ToStr().Len() = 2 + myVidLevel = myVidLevel / 10 + end if + myVidLevel = myVidLevel.ToStr() + print "myVidLevel =", myVidLevel + print "type(myVidLevel) =", type(myVidLevel) end if - myVidLevel = myVidLevel.ToStr() - print "myVidLevel =", myVidLevel - print "type(myVidLevel) =", type(myVidLevel) if isValid(myVidProfile) and isValid(myVidLevel) canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer, Profile: myVidProfile, Level: myVidLevel }) From 91de2a8d5a8dc6a4d2d68325801bd4323bfff1ef Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Thu, 31 Aug 2023 17:04:11 -0700 Subject: [PATCH 29/60] only check mpeg2/4 if asked. mpeg4 overrides what the device thinks + test device for more codecs --- source/utils/deviceCapabilities.brs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index e4340e5f..fbc236db 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -778,8 +778,15 @@ function GetDirectPlayProfiles() as object } } ' all possible codecs - videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1", "mpeg4", "mpeg2"] - audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3"] + videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1", "h263", "mpeg1"] + audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3", "mpg123"] + ' respect user settings + if m.global.session.user.settings["playback.mpeg4"] + videoCodecs.push("mpeg4") + end if + if m.global.session.user.settings["playback.mpeg2"] + videoCodecs.push("mpeg2") + end if ' check video codecs for each container for each container in supportedCodecs for each videoCodec in videoCodecs @@ -797,7 +804,7 @@ function GetDirectPlayProfiles() as object end for end for - ' user settings override what the device thinks + ' user setting overrides if m.global.session.user.settings["playback.mpeg4"] for each container in supportedCodecs if not arrayHasValue(supportedCodecs[container]["video"], "mpeg4") @@ -805,13 +812,6 @@ function GetDirectPlayProfiles() as object end if end for end if - if m.global.session.user.settings["playback.mpeg2"] - for each container in supportedCodecs - if not arrayHasValue(supportedCodecs[container]["video"], "mpeg2video") - supportedCodecs[container]["video"].push("mpeg2video") - end if - end for - end if ' check audio codecs for each container for each container in supportedCodecs From 63e49bbc8e357b32e6540fc47e626454d49b3f3d Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 09:53:36 -0400 Subject: [PATCH 30/60] only use aac for 2 channel audio + update surround sound transcoding logic --- source/utils/deviceCapabilities.brs | 90 +++++++++++++---------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index fbc236db..eee20260 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -70,7 +70,7 @@ function getDeviceProfile() as object maxAudioChannels = "2" ' jellyfin expects this as a string ' in order of preference from left to right audioCodecs = ["mp3", "vorbis", "opus", "flac", "alac", "ac4", "pcm", "wma", "wmapro"] - surroundSoundCodecs = ["eac3", "ac3", "dts", "vorbis"] + surroundSoundCodecs = ["eac3", "ac3", "dts"] surroundSoundCodec = invalid if di.GetAudioOutputChannel() = "5.1 surround" maxAudioChannels = "6" @@ -311,14 +311,14 @@ function getDeviceProfile() as object ' build TranscodingProfiles ' - ' always add mp3 to TranscodingProfile for music + ' add mp3 to TranscodingProfile for music deviceProfile.TranscodingProfiles.push({ "Container": "mp3", "Type": "Audio", "AudioCodec": "mp3", "Context": "Streaming", "Protocol": "http", - "MaxAudioChannels": maxAudioChannels + "MaxAudioChannels": "2" }) deviceProfile.TranscodingProfiles.push({ "Container": "mp3", @@ -326,33 +326,24 @@ function getDeviceProfile() as object "AudioCodec": "mp3", "Context": "Static", "Protocol": "http", - "MaxAudioChannels": maxAudioChannels + "MaxAudioChannels": "2" }) - - audioCodec = invalid - if surroundSoundCodec = invalid - ' use aac for all 2 channel transcoding - audioCodec = "aac" - else - ' use best available codec for all multichannel transcoding - audioCodec = surroundSoundCodec - end if - + ' add aac to TranscodingProfile for stereo audio deviceProfile.TranscodingProfiles.push({ - "Container": audioCodec, + "Container": "ts", "Type": "Audio", - "AudioCodec": audioCodec, + "AudioCodec": "aac", "Context": "Streaming", "Protocol": "http", - "MaxAudioChannels": maxAudioChannels + "MaxAudioChannels": "2" }) deviceProfile.TranscodingProfiles.push({ - "Container": audioCodec, + "Container": "ts", "Type": "Audio", - "AudioCodec": audioCodec, + "AudioCodec": "aac", "Context": "Static", "Protocol": "http", - "MaxAudioChannels": maxAudioChannels + "MaxAudioChannels": "2" }) tsArray = { @@ -379,40 +370,37 @@ function getDeviceProfile() as object } ' surround sound - ' move preferred surround sound codec to front of string if surroundSoundCodec <> invalid - ' search codec strings for our preferred codec - tsCodecStringPosition = tsArray.AudioCodec.Instr(0, "," + surroundSoundCodec) - mp4CodecStringPosition = mp4Array.AudioCodec.Instr(0, "," + surroundSoundCodec) - ' ts - if tsCodecStringPosition <> -1 - ' remove codec from string - tsArray.AudioCodec.Replace("," + surroundSoundCodec, "") - end if - ' check beginning of array before adding to it - if tsArray.AudioCodec.Instr(0, surroundSoundCodec) = -1 - ' put codec in front of string - if tsArray.AudioCodec = "" - tsArray.AudioCodec = surroundSoundCodec - else - tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec - end if - end if - ' mp4 - if mp4CodecStringPosition <> -1 - ' remove codec from string - mp4Array.AudioCodec.Replace("," + surroundSoundCodec, "") - end if - ' check beginning of array before adding to it - if mp4Array.AudioCodec.Instr(0, surroundSoundCodec) = -1 - ' put codec in front of string - if mp4Array.AudioCodec = "" - mp4Array.AudioCodec = surroundSoundCodec - else - mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec - end if + ' add preferred surround sound codec to TranscodingProfile + deviceProfile.TranscodingProfiles.push({ + "Container": surroundSoundCodec, + "Type": "Audio", + "AudioCodec": surroundSoundCodec, + "Context": "Streaming", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) + deviceProfile.TranscodingProfiles.push({ + "Container": surroundSoundCodec, + "Type": "Audio", + "AudioCodec": surroundSoundCodec, + "Context": "Static", + "Protocol": "http", + "MaxAudioChannels": maxAudioChannels + }) + + ' put codec in front of AudioCodec string + if tsArray.AudioCodec = "" + tsArray.AudioCodec = surroundSoundCodec + else + tsArray.AudioCodec = surroundSoundCodec + "," + tsArray.AudioCodec end if + if mp4Array.AudioCodec = "" + mp4Array.AudioCodec = surroundSoundCodec + else + mp4Array.AudioCodec = surroundSoundCodec + "," + mp4Array.AudioCodec + end if end if deviceProfile.TranscodingProfiles.push(tsArray) From 41060d10bb13f0dba626949c32a8ed29939b5ca6 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 10:26:08 -0400 Subject: [PATCH 31/60] respect "Profile Level Support" settings + add setting to use DTS for transcoding instead of the default EAC3 --- locale/en_US/translations.ts | 19 +++++++++++++-- settings/settings.json | 17 ++++++++++++-- source/utils/deviceCapabilities.brs | 36 +++++++++++++++++++---------- 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index aca5b0a2..a243a0e9 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1031,8 +1031,8 @@ Enable Limit - Enable or disable the 'Maximum Bitrate' setting. - Enable or disable the 'Maximum Bitrate' setting. + Enable or disable the 'Maximum Bitrate' setting. + Enable or disable the 'Maximum Bitrate' setting. Bitrate Limit @@ -1114,5 +1114,20 @@ Set how many seconds before the end of an episode the Next Episode button should appear. Set to 0 to disable. Settings Menu - Description for option + + Choose your preferred audio codec when transcoding multichannel audio. + Choose your preferred audio codec when transcoding multichannel audio. + Settings Menu - Description for option + + + Force all transcodes to use DTS instead of the default EAC3. The device must support DTS for this setting to have an effect. + Force all transcodes to use DTS instead of the default EAC3. The device must support DTS for this setting to have an effect. + Settings Menu - Description for option + + + Audio Codec Support + Audio Codec Support + Settings Menu - Title of option + \ No newline at end of file diff --git a/settings/settings.json b/settings/settings.json index efdf13c4..d173965f 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -24,7 +24,20 @@ ] }, { - "title": "Codec Support", + "title": "Audio Codec Support", + "description": "Choose your preferred audio codec when transcoding multichannel audio.", + "children": [ + { + "title": "DTS", + "description": "Force all transcodes to use DTS instead of the default EAC3. The device must support DTS for this setting to have an effect.", + "settingName": "playback.forceDTS", + "type": "bool", + "default": "false" + } + ] + }, + { + "title": "Video Codec Support", "description": "Enable or disable Direct Play support for certain codecs.", "children": [ { @@ -51,7 +64,7 @@ ] }, { - "title": "Profile Level Support", + "title": "Video Profile Level Support", "description": "Attempt Direct Play of potentially unsupported profile levels", "children": [ { diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index eee20260..d0687905 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -71,6 +71,10 @@ function getDeviceProfile() as object ' in order of preference from left to right audioCodecs = ["mp3", "vorbis", "opus", "flac", "alac", "ac4", "pcm", "wma", "wmapro"] surroundSoundCodecs = ["eac3", "ac3", "dts"] + if m.global.session.user.settings["playback.forceDTS"] = true + surroundSoundCodecs = ["dts", "eac3", "ac3"] + end if + surroundSoundCodec = invalid if di.GetAudioOutputChannel() = "5.1 surround" maxAudioChannels = "6" @@ -473,15 +477,19 @@ function getDeviceProfile() as object "Property": "Height", "Value": m.global.device.videoHeight, "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "VideoLevel", - "Value": h264LevelString, - "IsRequired": false } ] } + ' check user setting before adding video level restrictions + if not m.global.session.user.settings["playback.tryDirect.h264ProfileLevel"] + codecProfileArray.Conditions.push({ + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": h264LevelString, + "IsRequired": false + }) + end if + bitRateArray = GetBitRateLimit("h264") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) @@ -657,12 +665,6 @@ function getDeviceProfile() as object "Value": hevcVideoRangeTypes, "IsRequired": false }, - { - "Condition": "LessThanEqual", - "Property": "VideoLevel", - "Value": hevcLevelString, - "IsRequired": false - }, { "Condition": "LessThanEqual", "Property": "Width", @@ -678,6 +680,16 @@ function getDeviceProfile() as object ] } + ' check user setting before adding VideoLevel restrictions + if not m.global.session.user.settings["playback.tryDirect.hevcProfileLevel"] + codecProfileArray.Conditions.push({ + "Condition": "LessThanEqual", + "Property": "VideoLevel", + "Value": hevcLevelString, + "IsRequired": false + }) + end if + bitRateArray = GetBitRateLimit("h265") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) From 63efcec4314a844d6ffc3d47536d03d5b883f4fb Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 11:42:49 -0400 Subject: [PATCH 32/60] Fix bad merge --- locale/en_US/translations.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 73707d3f..4dc3c490 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1128,10 +1128,11 @@ Audio Codec Support Audio Codec Support Settings Menu - Title of option - - Direct playing - Direct playing - + + + Direct playing + Direct playing + The source file is entirely compatible with this client and the session is receiving the file without modifications. The source file is entirely compatible with this client and the session is receiving the file without modifications. From 8a11eaaa035db7e431f9d1ba3a00084a8e13cc57 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 11:53:54 -0400 Subject: [PATCH 33/60] add some comments --- source/utils/deviceCapabilities.brs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index d0687905..1f432342 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -490,10 +490,12 @@ function getDeviceProfile() as object }) end if + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("h264") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) end if + deviceProfile.CodecProfiles.push(codecProfileArray) ' MPEG2 @@ -532,10 +534,13 @@ function getDeviceProfile() as object }, ] } + + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("mpeg2") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if @@ -603,10 +608,13 @@ function getDeviceProfile() as object }, ] } + + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("av1") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if @@ -690,10 +698,12 @@ function getDeviceProfile() as object }) end if + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("h265") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if @@ -738,10 +748,12 @@ function getDeviceProfile() as object ] } + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("vp9") if bitRateArray.count() > 0 codecProfileArray.Conditions.push(bitRateArray) end if + deviceProfile.CodecProfiles.push(codecProfileArray) end if From 5be0fea3fbaf124016e0f94405d453fd23ac73e6 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 11:57:19 -0400 Subject: [PATCH 34/60] alphabetize Playback settings --- settings/settings.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/settings/settings.json b/settings/settings.json index 99491f78..1d9d17a3 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -3,6 +3,19 @@ "title": "Playback", "description": "Settings relating to playback and supported codec and media types.", "children": [ + { + "title": "Audio Codec Support", + "description": "Choose your preferred audio codec when transcoding multichannel audio.", + "children": [ + { + "title": "DTS", + "description": "Force all transcodes to use DTS instead of the default EAC3. The device must support DTS for this setting to have an effect.", + "settingName": "playback.forceDTS", + "type": "bool", + "default": "false" + } + ] + }, { "title": "Bitrate Limit", "description": "Configure the maximum playback bitrate.", @@ -23,19 +36,6 @@ } ] }, - { - "title": "Audio Codec Support", - "description": "Choose your preferred audio codec when transcoding multichannel audio.", - "children": [ - { - "title": "DTS", - "description": "Force all transcodes to use DTS instead of the default EAC3. The device must support DTS for this setting to have an effect.", - "settingName": "playback.forceDTS", - "type": "bool", - "default": "false" - } - ] - }, { "title": "Video Codec Support", "description": "Enable or disable Direct Play support for certain codecs.", From b1eaa25273896232e9168aa1242471d4697b15a7 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 12:45:00 -0400 Subject: [PATCH 35/60] add maximum resolution user setting --- locale/en_US/translations.ts | 30 +++++-- settings/settings.json | 41 +++++++++ source/utils/deviceCapabilities.brs | 135 ++++++++++++++-------------- 3 files changed, 136 insertions(+), 70 deletions(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 4dc3c490..363beafd 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1129,14 +1129,34 @@ Audio Codec Support Settings Menu - Title of option - - Direct playing - Direct playing - + + Direct playing + Direct playing + The source file is entirely compatible with this client and the session is receiving the file without modifications. The source file is entirely compatible with this client and the session is receiving the file without modifications. Direct play info box text in GetPlaybackInfoTask.brs + + Maximum Resolution + Maximum Resolution + User Setting - Title + + + Set the maximum resolution when playing video files on this device. + Set the maximum resolution when playing video files on this device. + User Setting - Description + + + Off - Attempt to play all resolutions + Off - Attempt to play all resolutions + User Setting - Option title + + + Auto - Use TV resolution + Auto - Use TV resolution + User Setting - Option title + - + \ No newline at end of file diff --git a/settings/settings.json b/settings/settings.json index 1d9d17a3..d0e1df0e 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -36,6 +36,47 @@ } ] }, + { + "title": "Maximum Resolution", + "description": "Set the maximum resolution when playing video files on this device.", + "settingName": "playback.resolution", + "type": "radio", + "default": "auto", + "options": [ + { + "title": "Off - Attempt to play all resolutions", + "id": "off" + }, + { + "title": "Auto - Use TV resolution", + "id": "auto" + }, + { + "title": "360p", + "id": "360" + }, + { + "title": "480p", + "id": "480" + }, + { + "title": "720p", + "id": "720" + }, + { + "title": "10800p", + "id": "1080" + }, + { + "title": "4k", + "id": "2160" + }, + { + "title": "8k", + "id": "4320" + } + ] + }, { "title": "Video Codec Support", "description": "Enable or disable Direct Play support for certain codecs.", diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 99074584..953c3f53 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -411,7 +411,42 @@ function getDeviceProfile() as object deviceProfile.TranscodingProfiles.push(mp4Array) ' Build CodecProfiles - ' + ' max resolution + maxResSetting = m.global.session.user.settings["playback.resolution"] + maxVideoHeight = invalid + maxVideoWidth = invalid + + maxVideoHeight = maxResSetting + + if maxResSetting = "auto" + maxVideoHeight = m.global.device.videoHeight + maxVideoWidth = m.global.device.videoWidth + else if maxResSetting = "360" + maxVideoWidth = "480" + else if maxResSetting = "480" + maxVideoWidth = "640" + else if maxResSetting = "720" + maxVideoWidth = "1280" + else if maxResSetting = "1080" + maxVideoWidth = "1920" + else if maxResSetting = "2160" + maxVideoWidth = "3840" + else if maxResSetting = "4320" + maxVideoWidth = "7680" + end if + + maxVideoHeightArray = { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": maxVideoWidth, + "IsRequired": true + } + maxVideoWidthArray = { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": maxVideoHeight, + "IsRequired": true + } ' H264 h264Mp4LevelSupported = 0.0 h264TsLevelSupported = 0.0 @@ -465,21 +500,15 @@ function getDeviceProfile() as object "Property": "VideoRangeType", "Value": h264VideoRangeTypes, "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": m.global.device.videoWidth, - "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": m.global.device.videoHeight, - "IsRequired": true } + ] } + ' set max resolution + if maxResSetting <> "off" + codecProfileArray.Conditions.push(maxVideoHeightArray) + codecProfileArray.Conditions.push(maxVideoWidthArray) + end if ' check user setting before adding video level restrictions if not m.global.session.user.settings["playback.tryDirect.h264ProfileLevel"] codecProfileArray.Conditions.push({ @@ -519,22 +548,16 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": mpeg2Levels.join("|"), "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": m.global.device.videoWidth, - "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": m.global.device.videoHeight, - "IsRequired": true - }, + } ] } + ' set max resolution + if maxResSetting <> "off" + codecProfileArray.Conditions.push(maxVideoHeightArray) + codecProfileArray.Conditions.push(maxVideoWidthArray) + end if + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("mpeg2") if bitRateArray.count() > 0 @@ -593,22 +616,16 @@ function getDeviceProfile() as object "Property": "VideoLevel", "Value": (120 * av1HighestLevel).ToStr(), "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": m.global.device.videoWidth, - "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": m.global.device.videoHeight, - "IsRequired": true - }, + } ] } + ' set max resolution + if maxResSetting <> "off" + codecProfileArray.Conditions.push(maxVideoHeightArray) + codecProfileArray.Conditions.push(maxVideoWidthArray) + end if + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("av1") if bitRateArray.count() > 0 @@ -672,22 +689,16 @@ function getDeviceProfile() as object "Property": "VideoRangeType", "Value": hevcVideoRangeTypes, "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": m.global.device.videoWidth, - "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": m.global.device.videoHeight, - "IsRequired": true - }, + } ] } + ' set max resolution + if maxResSetting <> "off" + codecProfileArray.Conditions.push(maxVideoHeightArray) + codecProfileArray.Conditions.push(maxVideoWidthArray) + end if + ' check user setting before adding VideoLevel restrictions if not m.global.session.user.settings["playback.tryDirect.hevcProfileLevel"] codecProfileArray.Conditions.push({ @@ -732,22 +743,16 @@ function getDeviceProfile() as object "Property": "VideoRangeType", "Value": vp9VideoRangeTypes, "IsRequired": false - }, - { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": m.global.device.videoWidth, - "IsRequired": true - }, - { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": m.global.device.videoHeight, - "IsRequired": true - }, + } ] } + ' set max resolution + if maxResSetting <> "off" + codecProfileArray.Conditions.push(maxVideoHeightArray) + codecProfileArray.Conditions.push(maxVideoWidthArray) + end if + ' set bitrate restrictions based on user settings bitRateArray = GetBitRateLimit("vp9") if bitRateArray.count() > 0 From 449cff412e7f5c9e617d1ad927366a13a7066f14 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 13:32:06 -0400 Subject: [PATCH 36/60] Set playback.tryDirect settings to false by default to prevent crashing - favor app stability over file compatibility --- settings/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/settings.json b/settings/settings.json index d0e1df0e..d24b63e8 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -113,14 +113,14 @@ "description": "Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.", "settingName": "playback.tryDirect.h264ProfileLevel", "type": "bool", - "default": "true" + "default": "false" }, { "title": "HEVC", "description": "Attempt Direct Play for HEVC media with unsupported profile levels before falling back to transcoding if it fails.", "settingName": "playback.tryDirect.hevcProfileLevel", "type": "bool", - "default": "true" + "default": "false" } ] }, From 464abddb2d9780d4cc16ba9d700b9c4daf7563aa Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 15:56:20 -0400 Subject: [PATCH 37/60] Stop auto converting user settings to int. Update codebase to convert to int as needed --- components/JFVideo.brs | 2 +- components/home/LoadItemsTask.brs | 2 +- source/utils/deviceCapabilities.brs | 28 +++++++++++++++------------- source/utils/session.bs | 18 +++++------------- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/components/JFVideo.brs b/components/JFVideo.brs index 788973d2..cebacb03 100644 --- a/components/JFVideo.brs +++ b/components/JFVideo.brs @@ -22,7 +22,7 @@ sub init() m.nextEpisodeButton = m.top.findNode("nextEpisode") m.nextEpisodeButton.text = tr("Next Episode") m.nextEpisodeButton.setFocus(false) - m.nextupbuttonseconds = m.global.session.user.settings["playback.nextupbuttonseconds"] + m.nextupbuttonseconds = m.global.session.user.settings["playback.nextupbuttonseconds"].ToInt() m.showNextEpisodeButtonAnimation = m.top.findNode("showNextEpisodeButton") m.hideNextEpisodeButtonAnimation = m.top.findNode("hideNextEpisodeButton") diff --git a/components/home/LoadItemsTask.brs b/components/home/LoadItemsTask.brs index ee42be1e..79bb2d38 100644 --- a/components/home/LoadItemsTask.brs +++ b/components/home/LoadItemsTask.brs @@ -73,7 +73,7 @@ sub loadItems() params["limit"] = 24 params["EnableTotalRecordCount"] = false - maxDaysInNextUp = m.global.session.user.settings["ui.details.maxdaysnextup"] + maxDaysInNextUp = m.global.session.user.settings["ui.details.maxdaysnextup"].ToInt() if isValid(maxDaysInNextUp) if maxDaysInNextUp > 0 dateToday = CreateObject("roDateTime") diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 953c3f53..05f01a20 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -421,18 +421,20 @@ function getDeviceProfile() as object if maxResSetting = "auto" maxVideoHeight = m.global.device.videoHeight maxVideoWidth = m.global.device.videoWidth - else if maxResSetting = "360" - maxVideoWidth = "480" - else if maxResSetting = "480" - maxVideoWidth = "640" - else if maxResSetting = "720" - maxVideoWidth = "1280" - else if maxResSetting = "1080" - maxVideoWidth = "1920" - else if maxResSetting = "2160" - maxVideoWidth = "3840" - else if maxResSetting = "4320" - maxVideoWidth = "7680" + else if maxResSetting <> "off" + if maxResSetting = "360" + maxVideoWidth = "480" + else if maxResSetting = "480" + maxVideoWidth = "640" + else if maxResSetting = "720" + maxVideoWidth = "1280" + else if maxResSetting = "1080" + maxVideoWidth = "1920" + else if maxResSetting = "2160" + maxVideoWidth = "3840" + else if maxResSetting = "4320" + maxVideoWidth = "7680" + end if end if maxVideoHeightArray = { @@ -878,7 +880,7 @@ end function function GetBitRateLimit(codec as string) as object if m.global.session.user.settings["playback.bitrate.maxlimited"] = true - userSetLimit = m.global.session.user.settings["playback.bitrate.limit"] + userSetLimit = m.global.session.user.settings["playback.bitrate.limit"].ToInt() if isValid(userSetLimit) and type(userSetLimit) = "Integer" and userSetLimit > 0 userSetLimit *= 1000000 return { diff --git a/source/utils/session.bs b/source/utils/session.bs index 4f2e2552..4f015751 100644 --- a/source/utils/session.bs +++ b/source/utils/session.bs @@ -234,25 +234,17 @@ namespace session end sub ' Saves the user setting to the global session. - ' This also converts strings to boolean and integer as necessary before saving to global session + ' This also converts strings to boolean as necessary before saving to global session sub Save(name as string, value as string) if name = invalid or value = invalid then return tmpSettingArray = m.global.session.user.settings convertedValue = value - ' convert to int - valueInteger = value.ToInt() - if value = "0" or valueInteger <> 0 - convertedValue = valueInteger - end if - ' convert to boolean - if type(value) = "String" - if value = "true" - convertedValue = true - else if value = "false" - convertedValue = false - end if + if value = "true" + convertedValue = true + else if value = "false" + convertedValue = false end if tmpSettingArray[name] = convertedValue From e730974721f6b9e032d9a6a9a2532fdb4a524d5d Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 15:57:03 -0400 Subject: [PATCH 38/60] Fix typo --- settings/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/settings.json b/settings/settings.json index d24b63e8..4e277250 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -64,7 +64,7 @@ "id": "720" }, { - "title": "10800p", + "title": "1080p", "id": "1080" }, { From bb9f9b84d307b8baed3dc41bb15686cfb38b77df Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Fri, 1 Sep 2023 15:59:17 -0400 Subject: [PATCH 39/60] remove debugging code --- components/ItemGrid/LoadVideoContentTask.brs | 98 -------------------- 1 file changed, 98 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index 5a200e38..0cf47b14 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -75,102 +75,6 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s return end if - print "meta =", meta - print "meta.json =", meta.json - print "meta.json.MediaSources[0] =", meta.json.MediaSources[0] - print "meta.json.MediaSources[0].MediaStreams[0] =", meta.json.MediaSources[0].MediaStreams[0] - print "meta.json.MediaSources[0].MediaStreams[1] =", meta.json.MediaSources[0].MediaStreams[1] - print "meta.json.MediaStreams[0] =", meta.json.MediaStreams[0] - devinfo = CreateObject("roDeviceInfo") - - myVideoCodec = meta.json.MediaSources[0].MediaStreams[0].Codec - print "myVideoCodec =", myVideoCodec - myContainer = meta.json.MediaSources[0].Container - print "myContainer =", myContainer - - myVidProfile = invalid - if isValid(meta.json.MediaSources[0].MediaStreams[0].Profile) - myVidProfile = Lcase(meta.json.MediaSources[0].MediaStreams[0].Profile) - print "myVidProfile =", myVidProfile - end if - - myVidLevel = invalid - if isValid(meta.json.MediaSources[0].MediaStreams[0].Level) - myVidLevel = meta.json.MediaSources[0].MediaStreams[0].Level - if myVidLevel.ToStr().Len() = 2 - myVidLevel = myVidLevel / 10 - end if - myVidLevel = myVidLevel.ToStr() - print "myVidLevel =", myVidLevel - print "type(myVidLevel) =", type(myVidLevel) - end if - - if isValid(myVidProfile) and isValid(myVidLevel) - canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer, Profile: myVidProfile, Level: myVidLevel }) - else if isValid(myVidProfile) - canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer, Profile: myVidProfile }) - else - canDecodeVid = devinfo.CanDecodeVideo({ Codec: myVideoCodec, Container: myContainer }) - end if - print "canDecodeVid =", canDecodeVid - print "canDecodeVid.Result =", canDecodeVid.Result - if canDecodeVid.Updated <> invalid - print "canDecodeVid.Updated =", canDecodeVid.Updated - print "canDecodeVid.[canDecodeVid.Updated] =", canDecodeVid.[canDecodeVid.Updated] - end if - - myAudioCodec = meta.json.MediaSources[0].MediaStreams[1].Codec - print "myAudioCodec =", myAudioCodec - - myAudioProfile = invalid - if isValid(meta.json.MediaSources[0].MediaStreams[1].Profile) - myAudioProfile = Lcase(meta.json.MediaSources[0].MediaStreams[1].Profile) - if myAudioProfile <> invalid and myAudioProfile <> "" - if myAudioCodec = "aac" - if myAudioProfile = "lc" - myAudioProfile = "mp2 lc" - else if myAudioProfile = "he" - myAudioProfile = "mp4 he" - end if - end if - end if - print "myAudioProfile =", myAudioProfile - end if - myAudioLevel = invalid - if isValid(meta.json.MediaSources[0].MediaStreams[1].Level) - myAudioLevel = meta.json.MediaSources[0].MediaStreams[1].Level - if myAudioLevel.ToStr().Len() = 2 - myAudioLevel = myAudioLevel / 10 - end if - myAudioLevel = myAudioLevel.ToStr() - print "myAudioLevel =", myAudioLevel - end if - myAudioChannels = meta.json.MediaSources[0].MediaStreams[1].Channels - print "myAudioChannels =", myAudioChannels - myAudioBitrate = meta.json.MediaSources[0].MediaStreams[1].BitRate - myAudioBitrate = myAudioBitrate / 1000 ' convert from bits per second to Kbit/sec - myAudioBitrate = StrToI(myAudioBitrate.ToStr()) - print "myAudioBitrate =", myAudioBitrate - print "type(myAudioBitrate) =", type(myAudioBitrate) - - if isValid(myAudioProfile) and myAudioLevel <> "0" - print "audio profile and level provided" - canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels, Profile: myAudioProfile, Level: myAudioLevel }) - else if isValid(myAudioProfile) - print "audio profile provided" - canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels, Profile: myAudioProfile }) - else - print "neither audio profile nor level provided" - canDecodeAud = devinfo.CanDecodeAudio({ Codec: myAudioCodec, Container: myContainer, ChCnt: myAudioChannels }) - end if - - print "canDecodeAud =", canDecodeAud - print "canDecodeAud.Result =", canDecodeAud.Result - if canDecodeAud.Updated <> invalid - print "canDecodeAud.Updated =", canDecodeAud.Updated - print "canDecodeAud.[canDecodeAud.Updated] =", canDecodeAud.[canDecodeAud.Updated] - end if - videotype = LCase(meta.type) if videotype = "episode" or videotype = "series" @@ -284,8 +188,6 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s if not fully_external video.content = authRequest(video.content) end if - - print "video.directPlaySupported =", video.directPlaySupported end sub sub addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external) From 7e69fab4362c9de49bd33959b99063d5089109c0 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 10:48:40 -0400 Subject: [PATCH 40/60] mpeg2 bugfix + force mpeg2 codec support if setting is enabled --- source/utils/deviceCapabilities.brs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 05f01a20..4d0652fe 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -535,7 +535,7 @@ function getDeviceProfile() as object mpeg2Levels = [] for each container in profileSupport for each level in profileSupport[container]["mpeg2"] - if mpeg2Levels[level] = invalid + if not arrayHasValue(mpeg2Levels, level) mpeg2Levels.push(level) end if end for @@ -799,13 +799,6 @@ function GetDirectPlayProfiles() as object ' all possible codecs videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1", "h263", "mpeg1"] audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3", "mpg123"] - ' respect user settings - if m.global.session.user.settings["playback.mpeg4"] - videoCodecs.push("mpeg4") - end if - if m.global.session.user.settings["playback.mpeg2"] - videoCodecs.push("mpeg2") - end if ' check video codecs for each container for each container in supportedCodecs for each videoCodec in videoCodecs @@ -813,8 +806,6 @@ function GetDirectPlayProfiles() as object if videoCodec = "hevc" supportedCodecs[container]["video"].push("hevc") supportedCodecs[container]["video"].push("h265") - else if videoCodec = "mpeg2" - supportedCodecs[container]["video"].push("mpeg2video") else ' device profile string matches codec string supportedCodecs[container]["video"].push(videoCodec) @@ -826,9 +817,12 @@ function GetDirectPlayProfiles() as object ' user setting overrides if m.global.session.user.settings["playback.mpeg4"] for each container in supportedCodecs - if not arrayHasValue(supportedCodecs[container]["video"], "mpeg4") - supportedCodecs[container]["video"].push("mpeg4") - end if + supportedCodecs[container]["video"].push("mpeg4") + end for + end if + if m.global.session.user.settings["playback.mpeg2"] + for each container in supportedCodecs + supportedCodecs[container]["video"].push("mpeg2video") end for end if From f8fc95418a54f50350cb609e3f45c8a2ea1d5f78 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 12:16:26 -0400 Subject: [PATCH 41/60] Add max res mode setting - allow user to set max res on all files or just transcoded files --- settings/settings.json | 92 +++++++++++++++++----------- source/utils/deviceCapabilities.brs | 95 ++++++++++++++++------------- 2 files changed, 111 insertions(+), 76 deletions(-) diff --git a/settings/settings.json b/settings/settings.json index 4e277250..ff94c637 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -37,45 +37,69 @@ ] }, { - "title": "Maximum Resolution", - "description": "Set the maximum resolution when playing video files on this device.", - "settingName": "playback.resolution", - "type": "radio", - "default": "auto", - "options": [ + "title": "Resolution", + "description": "Modify the mode and maximum resolution when playing video files on this device.", + "children": [ { - "title": "Off - Attempt to play all resolutions", - "id": "off" + "title": "Set Mode", + "description": "Restrict resolution on just transcoded files or all files.", + "settingName": "playback.resolution.mode", + "type": "radio", + "default": "transcoding", + "options": [ + { + "title": "All files", + "id": "everything" + }, + { + "title": "Transcoded files only", + "id": "transcoding" + } + ] }, { - "title": "Auto - Use TV resolution", - "id": "auto" - }, - { - "title": "360p", - "id": "360" - }, - { - "title": "480p", - "id": "480" - }, - { - "title": "720p", - "id": "720" - }, - { - "title": "1080p", - "id": "1080" - }, - { - "title": "4k", - "id": "2160" - }, - { - "title": "8k", - "id": "4320" + "title": "Maximum Resolution", + "description": "Set the maximum resolution when playing video files on this device.", + "settingName": "playback.resolution.max", + "type": "radio", + "default": "auto", + "options": [ + { + "title": "Off - Attempt to play all resolutions", + "id": "off" + }, + { + "title": "Auto - Use TV resolution", + "id": "auto" + }, + { + "title": "360p", + "id": "360" + }, + { + "title": "480p", + "id": "480" + }, + { + "title": "720p", + "id": "720" + }, + { + "title": "1080p", + "id": "1080" + }, + { + "title": "4k", + "id": "2160" + }, + { + "title": "8k", + "id": "4320" + } + ] } ] + }, { "title": "Video Codec Support", diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 4d0652fe..74d9ccf0 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -43,6 +43,11 @@ sub PostDeviceProfile() print "profile.DeviceProfile.TranscodingProfiles =" for each prof in profile.DeviceProfile.TranscodingProfiles print prof + if isValid(prof.Conditions) + for each condition in prof.Conditions + print condition + end for + end if end for print "profile.PlayableMediaTypes =", profile.PlayableMediaTypes print "profile.SupportedCommands =", profile.SupportedCommands @@ -314,6 +319,43 @@ function getDeviceProfile() as object } ' build TranscodingProfiles + ' max resolution + maxResSetting = m.global.session.user.settings["playback.resolution.max"] + maxResMode = m.global.session.user.settings["playback.resolution.mode"] + maxVideoHeight = maxResSetting + maxVideoWidth = invalid + + if maxResSetting = "auto" + maxVideoHeight = m.global.device.videoHeight + maxVideoWidth = m.global.device.videoWidth + else if maxResSetting <> "off" + if maxResSetting = "360" + maxVideoWidth = "480" + else if maxResSetting = "480" + maxVideoWidth = "640" + else if maxResSetting = "720" + maxVideoWidth = "1280" + else if maxResSetting = "1080" + maxVideoWidth = "1920" + else if maxResSetting = "2160" + maxVideoWidth = "3840" + else if maxResSetting = "4320" + maxVideoWidth = "7680" + end if + end if + + maxVideoHeightArray = { + "Condition": "LessThanEqual", + "Property": "Width", + "Value": maxVideoWidth, + "IsRequired": true + } + maxVideoWidthArray = { + "Condition": "LessThanEqual", + "Property": "Height", + "Value": maxVideoHeight, + "IsRequired": true + } ' ' add mp3 to TranscodingProfile for music deviceProfile.TranscodingProfiles.push({ @@ -373,6 +415,12 @@ function getDeviceProfile() as object "BreakOnNonKeyFrames": false } + ' always apply max res to transcoding profile + if maxResSetting <> "off" + tsArray.Conditions = [maxVideoHeightArray, maxVideoWidthArray] + mp4Array.Conditions = [maxVideoHeightArray, maxVideoWidthArray] + end if + ' surround sound if surroundSoundCodec <> invalid ' add preferred surround sound codec to TranscodingProfile @@ -411,44 +459,7 @@ function getDeviceProfile() as object deviceProfile.TranscodingProfiles.push(mp4Array) ' Build CodecProfiles - ' max resolution - maxResSetting = m.global.session.user.settings["playback.resolution"] - maxVideoHeight = invalid - maxVideoWidth = invalid - maxVideoHeight = maxResSetting - - if maxResSetting = "auto" - maxVideoHeight = m.global.device.videoHeight - maxVideoWidth = m.global.device.videoWidth - else if maxResSetting <> "off" - if maxResSetting = "360" - maxVideoWidth = "480" - else if maxResSetting = "480" - maxVideoWidth = "640" - else if maxResSetting = "720" - maxVideoWidth = "1280" - else if maxResSetting = "1080" - maxVideoWidth = "1920" - else if maxResSetting = "2160" - maxVideoWidth = "3840" - else if maxResSetting = "4320" - maxVideoWidth = "7680" - end if - end if - - maxVideoHeightArray = { - "Condition": "LessThanEqual", - "Property": "Width", - "Value": maxVideoWidth, - "IsRequired": true - } - maxVideoWidthArray = { - "Condition": "LessThanEqual", - "Property": "Height", - "Value": maxVideoHeight, - "IsRequired": true - } ' H264 h264Mp4LevelSupported = 0.0 h264TsLevelSupported = 0.0 @@ -507,7 +518,7 @@ function getDeviceProfile() as object ] } ' set max resolution - if maxResSetting <> "off" + if maxResMode = "everything" and maxResSetting <> "off" codecProfileArray.Conditions.push(maxVideoHeightArray) codecProfileArray.Conditions.push(maxVideoWidthArray) end if @@ -555,7 +566,7 @@ function getDeviceProfile() as object } ' set max resolution - if maxResSetting <> "off" + if maxResMode = "everything" and maxResSetting <> "off" codecProfileArray.Conditions.push(maxVideoHeightArray) codecProfileArray.Conditions.push(maxVideoWidthArray) end if @@ -623,7 +634,7 @@ function getDeviceProfile() as object } ' set max resolution - if maxResSetting <> "off" + if maxResMode = "everything" and maxResSetting <> "off" codecProfileArray.Conditions.push(maxVideoHeightArray) codecProfileArray.Conditions.push(maxVideoWidthArray) end if @@ -696,7 +707,7 @@ function getDeviceProfile() as object } ' set max resolution - if maxResSetting <> "off" + if maxResMode = "everything" and maxResSetting <> "off" codecProfileArray.Conditions.push(maxVideoHeightArray) codecProfileArray.Conditions.push(maxVideoWidthArray) end if @@ -750,7 +761,7 @@ function getDeviceProfile() as object } ' set max resolution - if maxResSetting <> "off" + if maxResMode = "everything" and maxResSetting <> "off" codecProfileArray.Conditions.push(maxVideoHeightArray) codecProfileArray.Conditions.push(maxVideoWidthArray) end if From 4c303daedfc7f4d02c6e793780eb71aaa6e08e8f Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 19:42:21 -0400 Subject: [PATCH 42/60] save device model details to global and parse out serial --- source/utils/globals.brs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/utils/globals.brs b/source/utils/globals.brs index f53a6852..943984c7 100644 --- a/source/utils/globals.brs +++ b/source/utils/globals.brs @@ -40,9 +40,13 @@ end sub ' Save information from roDeviceInfo to m.global.device sub SaveDeviceToGlobal() deviceInfo = CreateObject("roDeviceInfo") + ' remove special characters regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "") filteredFriendly = regex.ReplaceAll(deviceInfo.getFriendlyName(), "") + ' parse out serial + displayName = deviceInfo.getModelDisplayName() + deviceSerial = Mid(filteredFriendly, len(displayName) + 4) ' determine max playback resolution ' https://developer.roku.com/en-ca/docs/references/brightscript/interfaces/ifdeviceinfo.md#getvideomode-as-string videoMode = deviceInfo.GetVideoMode() @@ -94,14 +98,17 @@ sub SaveDeviceToGlobal() else if videoHeight = "4320" bitDepth = 12 end if + m.global.addFields({ device: { id: deviceInfo.getChannelClientID(), uuid: deviceInfo.GetRandomUUID(), - name: deviceInfo.getModelDisplayName(), + name: displayName, friendlyName: filteredFriendly, model: deviceInfo.GetModel(), modelType: deviceInfo.GetModelType(), + modelDetails: deviceInfo.GetModelDetails(), + serial: deviceSerial, osVersion: deviceInfo.GetOSVersion(), locale: deviceInfo.GetCurrentLocale(), clockFormat: deviceInfo.GetClockFormat(), From 2d9dcd26b4e7028b5a8b3fed06eb18c360a5b9b4 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 19:45:00 -0400 Subject: [PATCH 43/60] add app url and device information to device profile --- source/utils/deviceCapabilities.brs | 49 ++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 74d9ccf0..84826ad6 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -13,7 +13,10 @@ function getDeviceCapabilities() as object "SupportedCommands": [], "SupportsPersistentIdentifier": false, "SupportsMediaControl": false, - "DeviceProfile": getDeviceProfile() + "SupportsContentUploading": false, + "SupportsSync": false, + "DeviceProfile": getDeviceProfile(), + "AppStoreUrl": "https://channelstore.roku.com/details/cc5e559d08d9ec87c5f30dcebdeebc12/jellyfin" } end function @@ -278,6 +281,22 @@ function getDeviceProfile() as object DirectPlayProfile = GetDirectPlayProfiles() deviceProfile = { + "Name": "Official Roku Client", + "Id": m.global.device.id, + "Identification": { + "FriendlyName": m.global.device.friendlyName, + "ModelNumber": m.global.device.model, + "SerialNumber": "string", + "ModelName": m.global.device.name, + "ModelDescription": "Type: " + m.global.device.modelType, + "Manufacturer": m.global.device.modelDetails.VendorName + }, + "FriendlyName": m.global.device.friendlyName, + "Manufacturer": m.global.device.modelDetails.VendorName, + "ModelName": m.global.device.name, + "ModelDescription": "Type: " + m.global.device.modelType, + "ModelNumber": m.global.device.model, + "SerialNumber": m.global.device.serial, "MaxStreamingBitrate": 120000000, "MaxStaticBitrate": 100000000, "MusicStreamingTranscodingBitrate": 192000, @@ -285,18 +304,17 @@ function getDeviceProfile() as object "TranscodingProfiles": [], "ContainerProfiles": [], "CodecProfiles": [ - ' { - ' "Type": "VideoAudio", - ' "Codec": DirectPlayProfile[1].AudioCodec, ' Use supported MKV Audio list - ' "Conditions": [ - ' { - ' "Condition": "LessThanEqual", - ' "Property": "AudioChannels", - ' "Value": maxAudioChannels, - ' "IsRequired": false - ' } - ' ] - ' } + { + "Type": "VideoAudio", + "Conditions": [ + { + "Condition": "LessThanEqual", + "Property": "AudioChannels", + "Value": maxAudioChannels, + "IsRequired": false + } + ] + } ], "SubtitleProfiles": [ { @@ -364,7 +382,7 @@ function getDeviceProfile() as object "AudioCodec": "mp3", "Context": "Streaming", "Protocol": "http", - "MaxAudioChannels": "2" + "MaxAudioChannels": maxAudioChannels }) deviceProfile.TranscodingProfiles.push({ "Container": "mp3", @@ -372,9 +390,10 @@ function getDeviceProfile() as object "AudioCodec": "mp3", "Context": "Static", "Protocol": "http", - "MaxAudioChannels": "2" + "MaxAudioChannels": maxAudioChannels }) ' add aac to TranscodingProfile for stereo audio + ' NOTE: multichannel aac is not supported. only decode to stereo on some devices deviceProfile.TranscodingProfiles.push({ "Container": "ts", "Type": "Audio", From d0bbd696f60089ec00fb2dd4eeb0d18285acd284 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 19:45:48 -0400 Subject: [PATCH 44/60] update session card in dash to show model --- source/api/baserequest.brs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/api/baserequest.brs b/source/api/baserequest.brs index fdd00f20..fde121ac 100644 --- a/source/api/baserequest.brs +++ b/source/api/baserequest.brs @@ -198,7 +198,7 @@ end sub function authRequest(request as object) as object QUOTE = Chr(34) auth = "MediaBrowser" + " Client=" + QUOTE + "Jellyfin Roku" + QUOTE - auth = auth + ", Device=" + QUOTE + m.global.device.name + " (" + m.global.device.friendlyName + ")" + QUOTE + auth = auth + ", Device=" + QUOTE + m.global.device.name + " (" + m.global.device.model + ")" + QUOTE auth = auth + ", Version=" + QUOTE + m.global.app.version + QUOTE if m.global.session.user.id <> invalid From 95d82f589abef03f4f165dcb6d27d73aa60452a5 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Sun, 3 Sep 2023 20:01:28 -0400 Subject: [PATCH 45/60] Tweak new setting names and update translation file --- locale/en_US/translations.ts | 31 +++++++++++++++++++++++++++++++ settings/settings.json | 12 ++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 363beafd..283802e9 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1158,5 +1158,36 @@ Auto - Use TV resolution User Setting - Option title + + Mode + Mode + User Setting - Setting title + + + Value + Value + User Setting - Setting title + + + Configure the maximum resolution when playing video files on this device. + Configure the maximum resolution when playing video files on this device. + User Setting - Description + + + Apply max resolution to all files or only transcoded files. + Apply max resolution to all files or only transcoded files. + User Setting - Description + + + All files + All files + User Setting - Setting titlw + + + Only transcoded files + Only transcoded files + User Setting - Setting titlw + + \ No newline at end of file diff --git a/settings/settings.json b/settings/settings.json index ff94c637..44d34984 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -37,12 +37,12 @@ ] }, { - "title": "Resolution", - "description": "Modify the mode and maximum resolution when playing video files on this device.", + "title": "Maximum Resolution", + "description": "Configure the maximum resolution when playing video files on this device.", "children": [ { - "title": "Set Mode", - "description": "Restrict resolution on just transcoded files or all files.", + "title": "Mode", + "description": "Apply max resolution to all files or only transcoded files.", "settingName": "playback.resolution.mode", "type": "radio", "default": "transcoding", @@ -52,13 +52,13 @@ "id": "everything" }, { - "title": "Transcoded files only", + "title": "Only transcoded files", "id": "transcoding" } ] }, { - "title": "Maximum Resolution", + "title": "Value", "description": "Set the maximum resolution when playing video files on this device.", "settingName": "playback.resolution.max", "type": "radio", From 4da206fce30be9cddc52bd991cdea07fc16960f2 Mon Sep 17 00:00:00 2001 From: IceTheGameDev Date: Mon, 4 Sep 2023 09:32:00 +0000 Subject: [PATCH 46/60] Translated using Weblate (Romanian) Currently translated at 24.3% (61 of 251 strings) Translation: Jellyfin/Jellyfin Roku Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-roku/ro/ --- locale/ro/translations.ts | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/locale/ro/translations.ts b/locale/ro/translations.ts index 870ade0f..1399d37b 100644 --- a/locale/ro/translations.ts +++ b/locale/ro/translations.ts @@ -2613,5 +2613,98 @@ Save Credentials? Salvezi credenţialele? + + Error During Playback + Eroare în timpul redării + Dialog title when error occurs during playback + + + Title of Tab for options to sort library content + TAB_SORT + Sortați + + + Title of Tab for options to filter library content + TAB_FILTER + Filtrați + + + On Now + Pornit acum + + + Special Features + Caracteristici Speciale + + + Error Retrieving Content + Eroare în obținerea conținutului + Dialog title when unable to load Content from Server + + + Born + Născut/ă + + + More Like This + Mai Multe Ca Acesta + + + Age + Vârstă + + + Movies + Filme + + + There was an error retrieving the data for this item from the server. + A avut loc o eroare în obținerea datelor despre acest item al serverului. + Dialog detail when unable to load Content from Server + + + An error was encountered while playing this item. + A avut loc o eroare în timpul redării. + Dialog detail when error occurs during playback + + + Loading Channel Data + Se încarcă datele canalului + + + Error loading Channel Data + Eroare în încărcarea datelor canalului + + + IMDB_RATING + Recenzie IMDb + + + RELEASE_DATE + Data lansării + + + Died + Decedat/ă + + + Press 'OK' to Close + Apăsați 'OK' pentru a închide + + + Additional Parts + Părţi Adiţionale + Additional parts of a video + + + Sunday + duminică + Day of Week + + + Name or Title field of media item + TITLE + Nume + From 7aaabf35f0047639f5e064578fda3d66a13bb332 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:21:40 +0000 Subject: [PATCH 47/60] Update actions/checkout action to v4 --- .github/workflows/build-dev.yml | 2 +- .github/workflows/build-prod.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 4e6632b7..f95b90b5 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -12,7 +12,7 @@ jobs: dev: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3 with: node-version: "lts/*" diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index dc53f9e9..10a20ed0 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout master (the latest release) - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 with: ref: master - name: Install jq to parse json @@ -33,7 +33,7 @@ jobs: - name: Save old Makefile version run: awk 'BEGIN { FS=" = " } /^VERSION/ { print "oldMakeVersion="$2; }' Makefile >> $GITHUB_ENV - name: Checkout PR branch - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Save new package.json version run: echo "newPackVersion=$(jq -r ".version" package.json)" >> $GITHUB_ENV - name: package.json version must be updated @@ -61,7 +61,7 @@ jobs: prod: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3 with: node-version: "lts/*" From f359a7c3bf4604131c403e1b3de49875f9a7011e Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Mon, 4 Sep 2023 10:30:20 -0400 Subject: [PATCH 48/60] add user setting to disable HEVC codec #904 --- locale/en_US/translations.ts | 25 +++++++++-- settings/settings.json | 13 ++++++ source/utils/deviceCapabilities.brs | 69 ++++++++++++++++------------- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 283802e9..e4a3d6b0 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1181,13 +1181,32 @@ All files All files - User Setting - Setting titlw + User Setting - Setting title Only transcoded files Only transcoded files - User Setting - Setting titlw + User Setting - Setting title + + + Compatibility + Compatibility + User Setting - Setting title + + + Attempt to prevent playback failures. + Attempt to prevent playback failures. + User Setting - Setting description + + + Disable HEVC + Disable HEVC + User Setting - Setting title + + + Disable the HEVC codec on this device. This may improve playback for some devices (ultra). + Disable the HEVC codec on this device. This may improve playback for some devices (ultra). + User Setting - Setting description - \ No newline at end of file diff --git a/settings/settings.json b/settings/settings.json index 44d34984..f5240e70 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -36,6 +36,19 @@ } ] }, + { + "title": "Compatibility", + "description": "Attempt to prevent playback failures.", + "children": [ + { + "title": "Disable HEVC", + "description": "Disable the HEVC codec on this device. This may improve playback for some devices (ultra).", + "settingName": "playback.compatibility.disablehevc", + "type": "bool", + "default": "false" + } + ] + }, { "title": "Maximum Resolution", "description": "Configure the maximum resolution when playing video files on this device.", diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index 84826ad6..cdfc64b8 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -1,4 +1,5 @@ import "pkg:/source/utils/misc.brs" +import "pkg:/source/api/baserequest.brs" 'Device Capabilities for Roku. 'This will likely need further tweaking @@ -16,7 +17,8 @@ function getDeviceCapabilities() as object "SupportsContentUploading": false, "SupportsSync": false, "DeviceProfile": getDeviceProfile(), - "AppStoreUrl": "https://channelstore.roku.com/details/cc5e559d08d9ec87c5f30dcebdeebc12/jellyfin" + "AppStoreUrl": "https://channelstore.roku.com/details/cc5e559d08d9ec87c5f30dcebdeebc12/jellyfin", + "IconUrl": "https://github.com/jellyfin/jellyfin-web/blob/master/src/assets/img/devices/roku.svg" } end function @@ -126,38 +128,40 @@ function getDeviceProfile() as object end for end for - ' HEVC / h265 - hevcProfiles = ["main", "main 10"] - hevcLevels = ["4.1", "5.0", "5.1"] addHevc = false - for each container in profileSupport - for each profile in hevcProfiles - for each level in hevcLevels - if di.CanDecodeVideo({ Codec: "hevc", Container: container, Profile: profile, Level: level }).Result - addHevc = true - profileSupport[container] = updateProfileArray(profileSupport[container], "hevc", profile, level) - profileSupport[container] = updateProfileArray(profileSupport[container], "h265", profile, level) - if container = "mp4" - ' check for codec string before adding it - if mp4VideoCodecs.Instr(0, "h265,") = -1 - mp4VideoCodecs = "h265," + mp4VideoCodecs - end if - if mp4VideoCodecs.Instr(0, "hevc,") = -1 - mp4VideoCodecs = "hevc," + mp4VideoCodecs - end if - else if container = "ts" - ' check for codec string before adding it - if tsVideoCodecs.Instr(0, "h265,") = -1 - tsVideoCodecs = "h265," + tsVideoCodecs - end if - if tsVideoCodecs.Instr(0, "hevc,") = -1 - tsVideoCodecs = "hevc," + tsVideoCodecs + if m.global.session.user.settings["playback.compatibility.disablehevc"] = false + ' HEVC / h265 + hevcProfiles = ["main", "main 10"] + hevcLevels = ["4.1", "5.0", "5.1"] + for each container in profileSupport + for each profile in hevcProfiles + for each level in hevcLevels + if di.CanDecodeVideo({ Codec: "hevc", Container: container, Profile: profile, Level: level }).Result + addHevc = true + profileSupport[container] = updateProfileArray(profileSupport[container], "hevc", profile, level) + profileSupport[container] = updateProfileArray(profileSupport[container], "h265", profile, level) + if container = "mp4" + ' check for codec string before adding it + if mp4VideoCodecs.Instr(0, "h265,") = -1 + mp4VideoCodecs = "h265," + mp4VideoCodecs + end if + if mp4VideoCodecs.Instr(0, "hevc,") = -1 + mp4VideoCodecs = "hevc," + mp4VideoCodecs + end if + else if container = "ts" + ' check for codec string before adding it + if tsVideoCodecs.Instr(0, "h265,") = -1 + tsVideoCodecs = "h265," + tsVideoCodecs + end if + if tsVideoCodecs.Instr(0, "hevc,") = -1 + tsVideoCodecs = "hevc," + tsVideoCodecs + end if end if end if - end if + end for end for end for - end for + end if ' VP9 vp9Profiles = ["profile 0", "profile 2"] @@ -434,7 +438,7 @@ function getDeviceProfile() as object "BreakOnNonKeyFrames": false } - ' always apply max res to transcoding profile + ' apply max res to transcoding profile if maxResSetting <> "off" tsArray.Conditions = [maxVideoHeightArray, maxVideoWidthArray] mp4Array.Conditions = [maxVideoHeightArray, maxVideoWidthArray] @@ -827,8 +831,13 @@ function GetDirectPlayProfiles() as object } } ' all possible codecs - videoCodecs = ["h264", "mpeg4 avc", "vp8", "hevc", "vp9", "av1", "h263", "mpeg1"] + videoCodecs = ["h264", "mpeg4 avc", "vp8", "vp9", "av1", "h263", "mpeg1"] audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3", "mpg123"] + + if m.global.session.user.settings["playback.compatibility.disablehevc"] = false + videoCodecs.push("hevc") + end if + ' check video codecs for each container for each container in supportedCodecs for each videoCodec in videoCodecs From 94f284567a762a41b2c3ee1fb2a37bb863f94e35 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Mon, 4 Sep 2023 13:12:38 -0400 Subject: [PATCH 49/60] prevent crash when language is invalid --- components/ItemGrid/LoadVideoContentTask.brs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.brs b/components/ItemGrid/LoadVideoContentTask.brs index 0cf47b14..50a97855 100644 --- a/components/ItemGrid/LoadVideoContentTask.brs +++ b/components/ItemGrid/LoadVideoContentTask.brs @@ -405,8 +405,10 @@ function FindPreferredAudioStream(streams as dynamic) as integer if preferredLanguage <> invalid for i = 0 to streams.Count() - 1 - if LCase(streams[i].Type) = "audio" and LCase(streams[i].Language) = LCase(preferredLanguage) - return i + if LCase(streams[i].Type) = "audio" + if streams[i].Language <> invalid and LCase(streams[i].Language) = LCase(preferredLanguage) + return i + end if end if end for end if From 4d9f74e6a7313c8ae8c8ef067ac4bc43df0fd669 Mon Sep 17 00:00:00 2001 From: Charles Ewert Date: Mon, 4 Sep 2023 13:52:14 -0400 Subject: [PATCH 50/60] only direct play av1 when user setting is enabled --- source/utils/deviceCapabilities.brs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs index cdfc64b8..2501bd87 100644 --- a/source/utils/deviceCapabilities.brs +++ b/source/utils/deviceCapabilities.brs @@ -17,8 +17,7 @@ function getDeviceCapabilities() as object "SupportsContentUploading": false, "SupportsSync": false, "DeviceProfile": getDeviceProfile(), - "AppStoreUrl": "https://channelstore.roku.com/details/cc5e559d08d9ec87c5f30dcebdeebc12/jellyfin", - "IconUrl": "https://github.com/jellyfin/jellyfin-web/blob/master/src/assets/img/devices/roku.svg" + "AppStoreUrl": "https://channelstore.roku.com/details/cc5e559d08d9ec87c5f30dcebdeebc12/jellyfin" } end function @@ -830,10 +829,16 @@ function GetDirectPlayProfiles() as object video: [] } } - ' all possible codecs - videoCodecs = ["h264", "mpeg4 avc", "vp8", "vp9", "av1", "h263", "mpeg1"] + ' all possible codecs (besides those restricted by user settings) + videoCodecs = ["h264", "mpeg4 avc", "vp8", "vp9", "h263", "mpeg1"] audioCodecs = ["mp3", "mp2", "pcm", "lpcm", "wav", "ac3", "ac4", "aiff", "wma", "flac", "alac", "aac", "opus", "dts", "wmapro", "vorbis", "eac3", "mpg123"] + ' only try to direct play av1 if asked + if m.global.session.user.settings["playback.av1"] + videoCodecs.push("av1") + end if + + ' check if hevc is disabled if m.global.session.user.settings["playback.compatibility.disablehevc"] = false videoCodecs.push("hevc") end if From 1386d9b2c9b699cd7438e111b1b8753c6e7f8cc7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:37:58 +0000 Subject: [PATCH 51/60] Update actions/upload-artifact digest to a8a3f3a --- .github/workflows/build-dev.yml | 2 +- .github/workflows/build-prod.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index f95b90b5..90f01e7b 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -23,7 +23,7 @@ jobs: run: npm run ropm - name: Build app run: npm run build - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: Jellyfin-Roku-dev-${{ github.sha }} path: ${{ github.workspace }}/build/staging diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index 10a20ed0..69f6387d 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -72,7 +72,7 @@ jobs: run: npm run ropm - name: Build app for production run: npm run build-prod - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: Jellyfin-Roku-v${{ env.newManVersion }}-${{ github.sha }} path: ${{ github.workspace }}/build/staging From b4123e3848a1414f78a7eeaf84edceef421cf338 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:38:23 +0000 Subject: [PATCH 52/60] Update dependency brighterscript to v0.65.5 --- package-lock.json | 65 +++-------------------------------------------- package.json | 2 +- 2 files changed, 5 insertions(+), 62 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3ac7327..f928e412 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@rokucommunity/bslint": "0.8.9", - "brighterscript": "0.65.4", + "brighterscript": "0.65.5", "jshint": "2.13.6", "markdownlint-cli2": "0.9.2", "rimraf": "5.0.1", @@ -533,9 +533,9 @@ } }, "node_modules/brighterscript": { - "version": "0.65.4", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.4.tgz", - "integrity": "sha512-bp2aVhLOM1xRuZiyKx1WQp4JS8Fm6wfD7kAI4rE3YYMYeMhZ/SxTCvBtmTsaW/Z40oB8bBJXuKSuXgz2uF+ywQ==", + "version": "0.65.5", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.5.tgz", + "integrity": "sha512-xDkWIZhjTLhp6dVZ6lX7zWRyNvjdiAwZncJRnErSbqRhteNJFL7ic2UDJew9zCOYTQDrG7B85lpPpXc/1JlV+Q==", "dependencies": { "@rokucommunity/bslib": "^0.1.1", "@xml-tools/parser": "^1.0.7", @@ -4452,63 +4452,6 @@ "chevrotain": "7.1.1" } }, - "node_modules/ropm/node_modules/brighterscript": { - "version": "0.65.4", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.4.tgz", - "integrity": "sha512-bp2aVhLOM1xRuZiyKx1WQp4JS8Fm6wfD7kAI4rE3YYMYeMhZ/SxTCvBtmTsaW/Z40oB8bBJXuKSuXgz2uF+ywQ==", - "dev": true, - "dependencies": { - "@rokucommunity/bslib": "^0.1.1", - "@xml-tools/parser": "^1.0.7", - "array-flat-polyfill": "^1.0.1", - "chalk": "^2.4.2", - "chevrotain": "^7.0.1", - "chokidar": "^3.5.1", - "clear": "^0.1.0", - "coveralls-next": "^4.2.0", - "cross-platform-clear-console": "^2.3.0", - "debounce-promise": "^3.1.0", - "eventemitter3": "^4.0.0", - "fast-glob": "^3.2.12", - "file-url": "^3.0.0", - "fs-extra": "^8.1.0", - "jsonc-parser": "^2.3.0", - "long": "^3.2.0", - "luxon": "^2.5.2", - "minimatch": "^3.0.4", - "moment": "^2.23.0", - "p-settle": "^2.1.0", - "parse-ms": "^2.1.0", - "readline": "^1.3.0", - "require-relative": "^0.8.7", - "roku-deploy": "^3.10.3", - "serialize-error": "^7.0.1", - "source-map": "^0.7.4", - "vscode-languageserver": "7.0.0", - "vscode-languageserver-protocol": "3.16.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-uri": "^2.1.1", - "xml2js": "^0.5.0", - "yargs": "^16.2.0" - }, - "bin": { - "bsc": "dist/cli.js" - } - }, - "node_modules/ropm/node_modules/brighterscript/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/ropm/node_modules/chevrotain": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz", diff --git a/package.json b/package.json index 4e14ad88..71372750 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@rokucommunity/bslint": "0.8.9", - "brighterscript": "0.65.4", + "brighterscript": "0.65.5", "jshint": "2.13.6", "markdownlint-cli2": "0.9.2", "rimraf": "5.0.1", From b6dbd76b116b8ec62bd0f1bd17067479b61503bb Mon Sep 17 00:00:00 2001 From: 0qln Date: Thu, 7 Sep 2023 18:55:10 +0000 Subject: [PATCH 53/60] Translated using Weblate (German) Currently translated at 50.9% (128 of 251 strings) Translation: Jellyfin/Jellyfin Roku Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-roku/de/ --- locale/de_DE/translations.ts | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/locale/de_DE/translations.ts b/locale/de_DE/translations.ts index e8c5e5aa..ad7ce59f 100644 --- a/locale/de_DE/translations.ts +++ b/locale/de_DE/translations.ts @@ -11337,5 +11337,107 @@ Auswählen, wann Titel angezeigt werden sollen Settings Menu - Description for option + + Movies (Presentation) + Filme (Präsentation) + Movie library view option + + + Started at + Gestartet um + (Past Tense) For defining time when a program started today (e.g. Started at 08:00) + + + Movies (Grid) + Filme (Raster) + Movie library view option + + + An error was encountered while playing this item. Server did not provide required transcoding data. + Bei der Wiedergabe dieses Elements ist ein Fehler aufgetreten. Der Server hat die erforderlichen Transcodierungsdaten nicht bereitgestellt. + Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url + + + Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth. + Unterstützung der direkten Wiedergabe von MPEG-2-Inhalten (z. B. Live-TV). Dies verhindert die Transkodierung von MPEG-2-Inhalten, verbraucht aber deutlich mehr Bandbreite + Settings Menu - Description for option + + + Version + Version + + + MPEG-2 + MPEG-2 + Name of codec used in settings menu + + + MPEG-4 + MPEG-4 + Name of codec used in settings menu + + + Playback + Wiedergabe + Title for Playback section in user setting screen. + + + Special Features + Extras + + + Error Getting Playback Information + Fehler beim Abrufen der Wiedergabeinformationen + Dialog Title: Received error from server when trying to get information about the selected item for playback + + + Additional Parts + Zusätzliche Inhalte + Additional parts of a video + + + Started + Gestartet + (Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) + + + Channels + Sender + Menu option for showing Live TV Channel List + + + View Channel + Sender ansehen + + + Codec Support + Codec Unterstützung + Settings Menu - Title for settings group related to codec support + + + Enter the server name or IP address + Geben Sie den Servernamen oder die IP-Adresse ein + Title of KeyboardDialog when manually entering a server URL + + + The requested content does not exist on the server + Der angeforderte Inhalt existiert nicht auf dem Server + Content of message box when the requested content is not found on the server + + + Pick a Jellyfin server from the local network + Wählen Sie einen verfügbaren Jellyfin-Server in Ihrem lokalen Netzwerk aus: + Instructions on initial app launch when the user is asked to pick a server from a list + + + ...or enter server URL manually: + Fügen Sie bitte die Server-URL manuell ein, falls kein Server vorhanden ist: + Instructions on initial app launch when the user is asked to manually enter a server URL + + + Enable or disable Direct Play for optional codecs + Aktivieren oder Deaktivieren von Direct Play für optionale Codecs + Settings Menu - Title for settings group related to codec support + From e1984821ca4f184f97e16d264d0515c869f76832 Mon Sep 17 00:00:00 2001 From: Andrea Ciccarello Date: Sat, 9 Sep 2023 16:23:12 +0000 Subject: [PATCH 54/60] Translated using Weblate (Italian) Currently translated at 15.9% (40 of 251 strings) Translation: Jellyfin/Jellyfin Roku Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-roku/it/ --- locale/it_IT/translations.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locale/it_IT/translations.ts b/locale/it_IT/translations.ts index f5299609..2d765997 100644 --- a/locale/it_IT/translations.ts +++ b/locale/it_IT/translations.ts @@ -2190,5 +2190,13 @@ In onda Aired date label + + Change Server + Cambia Server + + + Sign Out + Scollegamento + From a77bdc0d1e37740452753938131cbef1551cfd1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:31:39 +0000 Subject: [PATCH 55/60] Pin actions/setup-java action to cd89f46 --- .github/workflows/roku-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml index e046b220..e91e833a 100644 --- a/.github/workflows/roku-analysis.yml +++ b/.github/workflows/roku-analysis.yml @@ -27,7 +27,7 @@ jobs: if: env.BRANCH_NAME == 'master' run: npm run build-prod - name: Use Java 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3 with: distribution: "temurin" java-version: "17" From 24202b59e3f96625509c696ffdce4b751287e3cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:31:45 +0000 Subject: [PATCH 56/60] Update actions/checkout digest to f43a0e5 --- .github/workflows/roku-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml index e046b220..4215f410 100644 --- a/.github/workflows/roku-analysis.yml +++ b/.github/workflows/roku-analysis.yml @@ -11,7 +11,7 @@ jobs: static: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 with: node-version: "lts/*" From 04a43d30306696ea9711b30b2588138f9f095868 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:37:08 +0000 Subject: [PATCH 57/60] Update actions/checkout action to v4 --- .github/workflows/roku-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml index fa314f4e..a557ac54 100644 --- a/.github/workflows/roku-analysis.yml +++ b/.github/workflows/roku-analysis.yml @@ -11,7 +11,7 @@ jobs: static: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 with: node-version: "lts/*" From aed68f1cf1a4db5d9462f032cb351aa20fc069ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:14:46 +0000 Subject: [PATCH 58/60] Update actions/setup-node digest to 5e21ff4 --- .github/workflows/roku-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml index a557ac54..0965214f 100644 --- a/.github/workflows/roku-analysis.yml +++ b/.github/workflows/roku-analysis.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 + - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3 with: node-version: "lts/*" cache: "npm" From ea7e4def5d738afbed0043956bd0372e6aa091c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:43:19 +0000 Subject: [PATCH 59/60] Update dependency @rokucommunity/bslint to v0.8.10 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f928e412..bca6fe3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "sob": "npm:slide-out-button@1.0.1" }, "devDependencies": { - "@rokucommunity/bslint": "0.8.9", + "@rokucommunity/bslint": "0.8.10", "brighterscript": "0.65.5", "jshint": "2.13.6", "markdownlint-cli2": "0.9.2", @@ -125,9 +125,9 @@ "integrity": "sha512-2ox6EUL+UTtccTbD4dbVjZK3QHa0PHCqpoKMF8lZz9ayzzEP3iVPF8KZR6hOi6bxsIcbGXVjqmtCVkpC4P9SrA==" }, "node_modules/@rokucommunity/bslint": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.9.tgz", - "integrity": "sha512-7rXHcGL8XVguqzjKtsUoRvnm/g6ySJwV+xGO9n8Mc1r1zNIvjMi9Spfz1Kh8zJTXwfxOkG1X68lMAtp9bk/gxA==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.10.tgz", + "integrity": "sha512-2aaWPtt5xACm7sIaKR5ctF7f3HrjKGfTVSAT6ZGdojDsXPQanHK2+XyYX20fQVdjJFVn414UCfVkUiaO0PTtog==", "dev": true, "dependencies": { "fs-extra": "^10.0.0", diff --git a/package.json b/package.json index 71372750..aefd6dea 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "sob": "npm:slide-out-button@1.0.1" }, "devDependencies": { - "@rokucommunity/bslint": "0.8.9", + "@rokucommunity/bslint": "0.8.10", "brighterscript": "0.65.5", "jshint": "2.13.6", "markdownlint-cli2": "0.9.2", From fd1e9d0ec241261adc8f2aef95d0e10fda4bab73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:12:53 +0000 Subject: [PATCH 60/60] Update dependency brighterscript-formatter to v1.6.32 --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index bca6fe3b..3f0ef534 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@rokucommunity/bslib": "0.1.1", "bgv": "npm:button-group-vert@1.0.2", - "brighterscript-formatter": "1.6.31", + "brighterscript-formatter": "1.6.32", "intKeyboard": "npm:integer-keyboard@1.0.12", "log": "npm:roku-log@0.11.1", "sob": "npm:slide-out-button@1.0.1" @@ -575,11 +575,11 @@ } }, "node_modules/brighterscript-formatter": { - "version": "1.6.31", - "resolved": "https://registry.npmjs.org/brighterscript-formatter/-/brighterscript-formatter-1.6.31.tgz", - "integrity": "sha512-XOc1LVAUXrWtCwAZeOUS/7gH5TbiEqZ0IwyAJcM7J5CxrFpMrw6NKg/Yox1oKaH1m11kcLR7KgckK5CTha0eYQ==", + "version": "1.6.32", + "resolved": "https://registry.npmjs.org/brighterscript-formatter/-/brighterscript-formatter-1.6.32.tgz", + "integrity": "sha512-7rbNmSsj2v8iv+iSWXczSg4hC7L1zxMEwo+jZcDaMDPu0TBt4zmmCpaT0WvvvYcE8fD9I1tJ4dlTaj61QnH0QQ==", "dependencies": { - "brighterscript": "^0.65.4", + "brighterscript": "^0.65.5", "glob-all": "^3.3.0", "jsonc-parser": "^3.0.0", "source-map": "^0.7.3", diff --git a/package.json b/package.json index aefd6dea..1412e72b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dependencies": { "@rokucommunity/bslib": "0.1.1", "bgv": "npm:button-group-vert@1.0.2", - "brighterscript-formatter": "1.6.31", + "brighterscript-formatter": "1.6.32", "intKeyboard": "npm:integer-keyboard@1.0.12", "log": "npm:roku-log@0.11.1", "sob": "npm:slide-out-button@1.0.1"