From 61afe6ba86e77bd0704765144b1b02992d4684b3 Mon Sep 17 00:00:00 2001 From: Ray <154766448+damontecres@users.noreply.github.com> Date: Sun, 19 Apr 2026 15:20:46 -0400 Subject: [PATCH] Port two changes to ExoPlayer device profile from the official app (#1263) ## Description Ports two changes in the official jellyfin android TV app here: - https://github.com/jellyfin/jellyfin-androidtv/pull/5240 - https://github.com/jellyfin/jellyfin-androidtv/pull/5186 The code changes are copied under GPL-2.0 from the above pull requests. ### Related issues Closes #1260 ### Testing Emulator mostly ## Screenshots N/A ## AI or LLM usage None --------- Co-authored-by: Niels van Velzen Co-authored-by: clams4shoes --- .../util/profile/DeviceProfileUtils.kt | 5 +- .../profile/MediaCodecCapabilitiesTest.kt | 84 +++++++++++++------ 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt index 6272af5a..2de5310e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/profile/DeviceProfileUtils.kt @@ -153,9 +153,10 @@ fun createDeviceProfile( type = DlnaProfileType.AUDIO context = EncodingContext.STREAMING - container = Codec.Container.MP3 + container = Codec.Container.TS + protocol = MediaStreamProtocol.HLS - audioCodec(Codec.Audio.MP3) + audioCodec(Codec.Audio.AAC) } // / Direct play profiles diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/profile/MediaCodecCapabilitiesTest.kt b/app/src/main/java/com/github/damontecres/wholphin/util/profile/MediaCodecCapabilitiesTest.kt index 23a3cc0c..ab4de03e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/profile/MediaCodecCapabilitiesTest.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/profile/MediaCodecCapabilitiesTest.kt @@ -1,6 +1,6 @@ package com.github.damontecres.wholphin.util.profile -// Copied from https://github.com/jellyfin/jellyfin-androidtv/blob/v0.19.4/app/src/main/java/org/jellyfin/androidtv/util/profile/MediaCodecCapabilitiesTest.kt +// Copied from https://github.com/jellyfin/jellyfin-androidtv/blob/v0.19.7/app/src/main/java/org/jellyfin/androidtv/util/profile/MediaCodecCapabilitiesTest.kt import android.content.Context import android.media.MediaCodecInfo.CodecProfileLevel @@ -41,11 +41,46 @@ class MediaCodecCapabilitiesTest( -1 } } - val Profile10: Int by lazy { + } + + // Some devices (e.g., Fire OS) may support AV1 below the official API level + // Use the platform constant if the API level is met; otherwise fall back to the literal value + // Reference: + // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/media/java/android/media/MediaCodecInfo.java + private object AV1ProfileLevel { + val ProfileMain10: Int by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + CodecProfileLevel.AV1ProfileMain10 + } else { + 0x2 + } + } + val ProfileMain10HDR10: Int by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + CodecProfileLevel.AV1ProfileMain10HDR10 + } else { + 0x1000 + } + } + val ProfileMain10HDR10Plus: Int by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + CodecProfileLevel.AV1ProfileMain10HDR10Plus + } else { + 0x2000 + } + } + val DolbyVisionProfile10: Int by lazy { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { CodecProfileLevel.DolbyVisionProfileDvav110 } else { - -1 + 0x400 + } + } + val Level5: Int by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + CodecProfileLevel.AV1Level5 + } else { + 0x1000 } } } @@ -90,41 +125,36 @@ class MediaCodecCapabilitiesTest( CodecProfileLevel.HEVCMainTierLevel62 to 186, ) - fun supportsAV1(): Boolean = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - hasCodecForMime(MediaFormat.MIMETYPE_VIDEO_AV1) + fun supportsAV1(): Boolean = hasCodecForMime(MimeTypes.VIDEO_AV1) fun supportsAV1Main10(): Boolean = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - hasDecoder( - MediaFormat.MIMETYPE_VIDEO_AV1, - CodecProfileLevel.AV1ProfileMain10, - CodecProfileLevel.AV1Level5, - ) + hasDecoder( + MimeTypes.VIDEO_AV1, + AV1ProfileLevel.ProfileMain10, + AV1ProfileLevel.Level5, + ) fun supportsAV1DolbyVision(): Boolean = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && hasDecoder( - MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, - DolbyVisionProfiles.Profile10, + MimeTypes.VIDEO_DOLBY_VISION, + AV1ProfileLevel.DolbyVisionProfile10, CodecProfileLevel.DolbyVisionLevelHd24, ) fun supportsAV1HDR10(): Boolean = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - hasDecoder( - MediaFormat.MIMETYPE_VIDEO_AV1, - CodecProfileLevel.AV1ProfileMain10HDR10, - CodecProfileLevel.AV1Level5, - ) + hasDecoder( + MimeTypes.VIDEO_AV1, + AV1ProfileLevel.ProfileMain10HDR10, + AV1ProfileLevel.Level5, + ) fun supportsAV1HDR10Plus(): Boolean = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - hasDecoder( - MediaFormat.MIMETYPE_VIDEO_AV1, - CodecProfileLevel.AV1ProfileMain10HDR10Plus, - CodecProfileLevel.AV1Level5, - ) + hasDecoder( + MimeTypes.VIDEO_AV1, + AV1ProfileLevel.ProfileMain10HDR10Plus, + AV1ProfileLevel.Level5, + ) fun supportsAVC(): Boolean = hasCodecForMime(MediaFormat.MIMETYPE_VIDEO_AVC)