Skip to content

Commit 5f2ca1d

Browse files
chore(docs): update code docs
1 parent 9d06293 commit 5f2ca1d

5 files changed

Lines changed: 105 additions & 38 deletions

docs/components_ItemGrid_LoadVideoContentTask.bs.html

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,12 +338,45 @@
338338
' artifacts. If the user preference is set, and the only reason the server says we need to
339339
' transcode is that the Encoding Level is not supported, then try to direct play but silently
340340
' fall back to the transcode if that fails.
341+
'
342+
' Resolution guard: Only attempt the level override when the source resolution is within the
343+
' codec's hardware ceiling. Many encoders tag 1080p content with inflated levels (e.g. 5.1)
344+
' even though the actual decode workload is within Roku's capability. But for resolutions
345+
' above the hardware limit (e.g. 4K h264), no level tolerance will help — skip the override.
346+
' Both height and width are checked to catch ultrawide or non-standard aspect ratios.
341347
if m.playbackInfo.MediaSources[0].MediaStreams.Count() > 0 and not isLive
342348
' Find first video stream - MediaStreams[0] might be subtitle/audio
343349
videoStream = getFirstVideoStream(m.playbackInfo.MediaSources[0].MediaStreams)
344350
if isValid(videoStream) and isValid(videoStream.codec)
345351
tryDirectPlay = userSettings.playbackTryDirectH264ProfileLevel and videoStream.codec = "h264"
346352
tryDirectPlay = tryDirectPlay or (userSettings.playbackTryDirectHevcProfileLevel and videoStream.codec = "hevc")
353+
354+
' Check source resolution against hardware ceiling before allowing override
355+
if tryDirectPlay
356+
sourceHeight = 0
357+
sourceWidth = 0
358+
359+
if isValid(videoStream.Height)
360+
sourceHeight = videoStream.Height
361+
if type(sourceHeight) = "roString" or type(sourceHeight) = "String"
362+
sourceHeight = sourceHeight.toInt()
363+
end if
364+
end if
365+
366+
if isValid(videoStream.Width)
367+
sourceWidth = videoStream.Width
368+
if type(sourceWidth) = "roString" or type(sourceWidth) = "String"
369+
sourceWidth = sourceWidth.toInt()
370+
end if
371+
end if
372+
373+
' h264: Roku hardware ceiling is 1080p — 4K h264 is physically impossible to decode
374+
' Check both dimensions to catch ultrawide or non-standard aspect ratios
375+
if videoStream.codec = "h264" and (sourceHeight > 1080 or sourceWidth > 1920)
376+
tryDirectPlay = false
377+
end if
378+
' hevc: no additional guard needed — Roku supports up to 4K hevc natively
379+
end if
347380
else
348381
tryDirectPlay = false
349382
end if

docs/data/search.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/module-deviceCapabilities.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/source_api_Items.bs.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@
146146
end for
147147

148148
for each codecProfile in postData.DeviceProfile.CodecProfiles
149-
if isValid(codecProfile.Codec) and LCase(codecProfile.Codec) = "h264"
150-
applyResolutionCapToProfile(codecProfile, h264CapHeight, h264CapWidth)
149+
' Codec field is a comma-separated list of aliases (e.g. "h264,avc")
150+
if isValid(codecProfile.Codec) and Instr(1, LCase(codecProfile.Codec), "h264") > 0
151+
applyResolutionCapToProfile(codecProfile, h264CapHeight, h264CapWidth, true)
151152
end if
152153
end for
153154
end if

docs/source_utils_deviceCapabilities.bs.html

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -858,15 +858,16 @@
858858
]
859859
}
860860

861-
' check user setting before adding video level restrictions
862-
if not globalUserSettings.playbackTryDirectH264ProfileLevel
863-
h264ProfileArray.Conditions.push({
864-
"Condition": "LessThanEqual",
865-
"Property": "VideoLevel",
866-
"Value": h264LevelString,
867-
"IsRequired": false
868-
})
869-
end if
861+
' Always send VideoLevel so the server produces compatible transcodes.
862+
' The playbackTryDirectH264ProfileLevel setting is handled client-side in
863+
' LoadVideoContentTask — if the only transcode reason is VideoLevelNotSupported,
864+
' the client will attempt direct play with a transcode fallback.
865+
h264ProfileArray.Conditions.push({
866+
"Condition": "LessThanEqual",
867+
"Property": "VideoLevel",
868+
"Value": h264LevelString,
869+
"IsRequired": false
870+
})
870871

871872
' set max resolution
872873
h264ProfileArray.Conditions.Append(resolutionConditions)
@@ -1026,15 +1027,16 @@
10261027
]
10271028
}
10281029

1029-
' check user setting before adding VideoLevel restrictions
1030-
if not globalUserSettings.playbackTryDirectHevcProfileLevel
1031-
hevcProfileArray.Conditions.push({
1032-
"Condition": "LessThanEqual",
1033-
"Property": "VideoLevel",
1034-
"Value": hevcLevelString,
1035-
"IsRequired": false
1036-
})
1037-
end if
1030+
' Always send VideoLevel so the server produces compatible transcodes.
1031+
' The playbackTryDirectHevcProfileLevel setting is handled client-side in
1032+
' LoadVideoContentTask — if the only transcode reason is VideoLevelNotSupported,
1033+
' the client will attempt direct play with a transcode fallback.
1034+
hevcProfileArray.Conditions.push({
1035+
"Condition": "LessThanEqual",
1036+
"Property": "VideoLevel",
1037+
"Value": hevcLevelString,
1038+
"IsRequired": false
1039+
})
10381040

10391041
' set max resolution
10401042
hevcProfileArray.Conditions.Append(resolutionConditions)
@@ -1288,33 +1290,64 @@
12881290

12891291
' Apply a paired Height + Width resolution cap to a codec profile's Conditions.
12901292
' No-ops if a Height condition at or below maxHeight already exists.
1293+
' When the existing cap is wider than maxHeight, replaces it (avoids duplicate conditions
1294+
' that can confuse the server's condition evaluation order).
12911295
' @param codecProfile {object} - The codec profile AA containing a Conditions array
12921296
' @param maxHeight {integer} - The max height to cap at (e.g. 1080)
12931297
' @param maxWidth {integer} - The max width to cap at (e.g. 1920)
12941298
' @param isRequired {boolean} - Whether the condition is required for direct play
12951299
sub applyResolutionCapToProfile(codecProfile as object, maxHeight as integer, maxWidth as integer, isRequired = false as boolean)
12961300
if not isValid(codecProfile) or not isValid(codecProfile.Conditions) then return
12971301

1298-
for each condition in codecProfile.Conditions
1299-
if isValid(condition.Property) and condition.Property = "Height"
1300-
if condition.Value.toInt() <= maxHeight
1301-
return ' Resolution cap already present
1302+
' Scan for existing Height/Width conditions and track their indices
1303+
heightIndex = -1
1304+
widthIndex = -1
1305+
for i = 0 to codecProfile.Conditions.count() - 1
1306+
condition = codecProfile.Conditions[i]
1307+
if isValid(condition.Property)
1308+
if condition.Property = "Height"
1309+
if condition.Value.toInt() <= maxHeight
1310+
return ' Existing cap is already at or below the target — nothing to do
1311+
end if
1312+
heightIndex = i
1313+
else if condition.Property = "Width"
1314+
widthIndex = i
13021315
end if
13031316
end if
13041317
end for
13051318

1306-
codecProfile.Conditions.push({
1307-
"Condition": "LessThanEqual",
1308-
"Property": "Height",
1309-
"Value": maxHeight.toStr(),
1310-
"IsRequired": isRequired
1311-
})
1312-
codecProfile.Conditions.push({
1313-
"Condition": "LessThanEqual",
1314-
"Property": "Width",
1315-
"Value": maxWidth.toStr(),
1316-
"IsRequired": isRequired
1317-
})
1319+
' Replace existing conditions in-place to avoid duplicates
1320+
if heightIndex >= 0
1321+
codecProfile.Conditions[heightIndex] = {
1322+
"Condition": "LessThanEqual",
1323+
"Property": "Height",
1324+
"Value": maxHeight.toStr(),
1325+
"IsRequired": isRequired
1326+
}
1327+
else
1328+
codecProfile.Conditions.push({
1329+
"Condition": "LessThanEqual",
1330+
"Property": "Height",
1331+
"Value": maxHeight.toStr(),
1332+
"IsRequired": isRequired
1333+
})
1334+
end if
1335+
1336+
if widthIndex >= 0
1337+
codecProfile.Conditions[widthIndex] = {
1338+
"Condition": "LessThanEqual",
1339+
"Property": "Width",
1340+
"Value": maxWidth.toStr(),
1341+
"IsRequired": isRequired
1342+
}
1343+
else
1344+
codecProfile.Conditions.push({
1345+
"Condition": "LessThanEqual",
1346+
"Property": "Width",
1347+
"Value": maxWidth.toStr(),
1348+
"IsRequired": isRequired
1349+
})
1350+
end if
13181351
end sub
13191352

13201353
' Receives and returns an assArray of supported profiles and levels for each video codec

0 commit comments

Comments
 (0)