Add setting to force transcoding above a max resolution (#1955)
## Description Adds a new setting to pick a maximum resolution (by height). Any playback that has a larger resolution will be transcoded down to the maximum resolution. Playback equal or less than the maximum resolution will not be affected and will direct play if otherwise able. This setting is also independent of the maximum bitrate (ie if max resolution is 4K and max bitrate is 2mbps, playing 4K will transcode). This setting is pretty much equivalent to the Web UI setting "Maximum Allowed Video Transcoding Resolution" with "Limit maximum supported video resolution" enabled. Note: this setting does not affect which version is chosen by default ### Related issues Closes #756 Closes #1594 ### Testing Manual emulator testing ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
b0d270380d
commit
559abe2458
6 changed files with 105 additions and 45 deletions
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.util.profile
|
|||
// Adapted from https://github.com/jellyfin/jellyfin-androidtv/blob/v0.19.4/app/src/main/java/org/jellyfin/androidtv/util/profile/deviceProfile.kt
|
||||
|
||||
import android.media.MediaCodecInfo
|
||||
import android.util.Size
|
||||
import androidx.media3.common.MimeTypes
|
||||
import com.github.damontecres.wholphin.util.profile.KnownDefects.supportsHi10P52
|
||||
import org.jellyfin.sdk.model.api.CodecType
|
||||
|
|
@ -61,6 +62,8 @@ val supportedAudioCodecs =
|
|||
// )
|
||||
// }
|
||||
|
||||
private fun Size.min(min: Int) = if (min > 0 && height > min) Size(width, min) else this
|
||||
|
||||
fun createDeviceProfile(
|
||||
mediaTest: MediaCodecCapabilitiesTest,
|
||||
maxBitrate: Int,
|
||||
|
|
@ -72,6 +75,7 @@ fun createDeviceProfile(
|
|||
decodeAv1: Boolean,
|
||||
jellyfinTenEleven: Boolean,
|
||||
preferAc3ForSurround: Boolean,
|
||||
maxResolution: Int,
|
||||
) = buildDeviceProfile {
|
||||
val allowedAudioCodecs =
|
||||
when {
|
||||
|
|
@ -106,10 +110,10 @@ fun createDeviceProfile(
|
|||
val supportsAV1 = mediaTest.supportsAV1()
|
||||
val supportsAV1Main10 = mediaTest.supportsAV1Main10()
|
||||
val supportsVC1 = mediaTest.supportsVc1()
|
||||
val maxResolutionAVC = mediaTest.getMaxResolution(MimeTypes.VIDEO_H264)
|
||||
val maxResolutionHevc = mediaTest.getMaxResolution(MimeTypes.VIDEO_H265)
|
||||
val maxResolutionAV1 = mediaTest.getMaxResolution(MimeTypes.VIDEO_AV1)
|
||||
val maxResolutionVC1 = mediaTest.getMaxResolution(MimeTypes.VIDEO_VC1)
|
||||
val maxResolutionAVC = mediaTest.getMaxResolution(MimeTypes.VIDEO_H264).min(maxResolution)
|
||||
val maxResolutionHevc = mediaTest.getMaxResolution(MimeTypes.VIDEO_H265).min(maxResolution)
|
||||
val maxResolutionAV1 = mediaTest.getMaxResolution(MimeTypes.VIDEO_AV1).min(maxResolution)
|
||||
val maxResolutionVC1 = mediaTest.getMaxResolution(MimeTypes.VIDEO_VC1).min(maxResolution)
|
||||
|
||||
// / HDR capabilities
|
||||
|
||||
|
|
@ -133,59 +137,43 @@ fun createDeviceProfile(
|
|||
|
||||
// / Transcoding profiles
|
||||
// Video
|
||||
if (preferAc3ForSurround) {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
|
||||
audioCodec(Codec.Audio.AC3)
|
||||
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
}
|
||||
} else {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
|
||||
if (preferAc3ForSurround) {
|
||||
audioCodec(*allowedAudioCodecs)
|
||||
} else {
|
||||
audioCodec(Codec.Audio.AC3)
|
||||
}
|
||||
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
|
||||
if (maxResolution > 0) {
|
||||
conditions {
|
||||
ProfileConditionValue.HEIGHT lowerThanOrEquals maxResolution
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Audio
|
||||
if (preferAc3ForSurround) {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
if (preferAc3ForSurround) {
|
||||
audioCodec(Codec.Audio.AC3)
|
||||
}
|
||||
} else {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
} else {
|
||||
audioCodec(Codec.Audio.AAC)
|
||||
}
|
||||
}
|
||||
|
|
@ -459,6 +447,24 @@ fun createDeviceProfile(
|
|||
}
|
||||
}
|
||||
|
||||
if (maxResolution > 0) {
|
||||
listOf(
|
||||
Codec.Video.MPEG,
|
||||
Codec.Video.MPEG2VIDEO,
|
||||
Codec.Video.VP8,
|
||||
Codec.Video.VP9,
|
||||
).forEach { codecName ->
|
||||
codecProfile {
|
||||
type = CodecType.VIDEO
|
||||
codec = codecName
|
||||
|
||||
conditions {
|
||||
ProfileConditionValue.HEIGHT lowerThanOrEquals maxResolution
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// / HDR exclude list
|
||||
|
||||
// TODO Use VideoRangeType enum with Jellyfin 10.11 based SDK
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue