|
858 | 858 | ] |
859 | 859 | } |
860 | 860 |
|
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 | + }) |
870 | 871 |
|
871 | 872 | ' set max resolution |
872 | 873 | h264ProfileArray.Conditions.Append(resolutionConditions) |
|
1026 | 1027 | ] |
1027 | 1028 | } |
1028 | 1029 |
|
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 | + }) |
1038 | 1040 |
|
1039 | 1041 | ' set max resolution |
1040 | 1042 | hevcProfileArray.Conditions.Append(resolutionConditions) |
|
1288 | 1290 |
|
1289 | 1291 | ' Apply a paired Height + Width resolution cap to a codec profile's Conditions. |
1290 | 1292 | ' 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). |
1291 | 1295 | ' @param codecProfile {object} - The codec profile AA containing a Conditions array |
1292 | 1296 | ' @param maxHeight {integer} - The max height to cap at (e.g. 1080) |
1293 | 1297 | ' @param maxWidth {integer} - The max width to cap at (e.g. 1920) |
1294 | 1298 | ' @param isRequired {boolean} - Whether the condition is required for direct play |
1295 | 1299 | sub applyResolutionCapToProfile(codecProfile as object, maxHeight as integer, maxWidth as integer, isRequired = false as boolean) |
1296 | 1300 | if not isValid(codecProfile) or not isValid(codecProfile.Conditions) then return |
1297 | 1301 |
|
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 |
1302 | 1315 | end if |
1303 | 1316 | end if |
1304 | 1317 | end for |
1305 | 1318 |
|
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 |
1318 | 1351 | end sub |
1319 | 1352 |
|
1320 | 1353 | ' Receives and returns an assArray of supported profiles and levels for each video codec |
|
0 commit comments