Fix Xbox UWP codec detection for AV1 and Opus
Xbox UWP applications use WebView2 which incorrectly reports support for AV1 video and Opus audio codecs. While the underlying Chromium engine supports these codecs, the Xbox UWP runtime cannot decode them, resulting in playback failures. This patch adds server-side filtering to remove AV1 and Opus from device profiles when an Xbox device is detected, ensuring the server will transcode these codecs to supported formats (H.264/AAC) instead of attempting direct play. Fixes #109
This commit is contained in:
@@ -146,6 +146,8 @@ public class MediaInfoController : BaseJellyfinApiController
|
||||
}
|
||||
}
|
||||
|
||||
_mediaInfoHelper.FilterXboxUnsupportedCodecs(profile, User.GetDeviceId());
|
||||
|
||||
// Copy params from posted body
|
||||
// TODO clean up when breaking API compatibility.
|
||||
userId ??= playbackInfoDto?.UserId;
|
||||
|
||||
@@ -425,6 +425,8 @@ public class MediaInfoHelper
|
||||
}
|
||||
}
|
||||
|
||||
FilterXboxUnsupportedCodecs(profile, httpContext.User.GetDeviceId());
|
||||
|
||||
if (profile is not null)
|
||||
{
|
||||
var item = _libraryManager.GetItemById<BaseItem>(request.ItemId)
|
||||
@@ -524,4 +526,67 @@ public class MediaInfoHelper
|
||||
|
||||
return maxBitrate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters unsupported codecs from Xbox device profiles.
|
||||
/// Xbox UWP WebView2 incorrectly reports support for AV1 and Opus codecs.
|
||||
/// </summary>
|
||||
/// <param name="profile">The device profile to filter.</param>
|
||||
/// <param name="deviceId">The device identifier.</param>
|
||||
public void FilterXboxUnsupportedCodecs(DeviceProfile? profile, string? deviceId)
|
||||
{
|
||||
if (profile is null || string.IsNullOrEmpty(deviceId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!deviceId.Contains("xbox", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Filtering unsupported codecs for Xbox device: {DeviceId}", deviceId);
|
||||
|
||||
if (profile.DirectPlayProfiles is not null)
|
||||
{
|
||||
foreach (var directPlay in profile.DirectPlayProfiles)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(directPlay.VideoCodec))
|
||||
{
|
||||
directPlay.VideoCodec = FilterCodecList(directPlay.VideoCodec, "av1");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(directPlay.AudioCodec))
|
||||
{
|
||||
directPlay.AudioCodec = FilterCodecList(directPlay.AudioCodec, "opus");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.TranscodingProfiles is not null)
|
||||
{
|
||||
foreach (var transcoding in profile.TranscodingProfiles)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(transcoding.VideoCodec))
|
||||
{
|
||||
transcoding.VideoCodec = FilterCodecList(transcoding.VideoCodec, "av1");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(transcoding.AudioCodec))
|
||||
{
|
||||
transcoding.AudioCodec = FilterCodecList(transcoding.AudioCodec, "opus");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string FilterCodecList(string codecList, string codecToRemove)
|
||||
{
|
||||
var codecs = codecList.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(c => c.Trim())
|
||||
.Where(c => !c.Equals(codecToRemove, StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
|
||||
return string.Join(',', codecs);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user