Merge pull request #895 from peterlodri92/fix-duplicate-hvec-trancoding

Improve HEVC codec handling in transcoding profiles
This commit is contained in:
Odd Stråbø 2024-08-10 21:26:57 +02:00 committed by GitHub
commit efe059a34d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 254 additions and 10 deletions

View file

@ -363,11 +363,10 @@ class PlayUtils(object):
) )
def get_directplay_video_codec(self): def get_directplay_video_codec(self):
codecs = ["h264", "hevc", "h265", "mpeg4", "mpeg2video", "vc1", "vp9", "av1"] codecs = ["h264", "hevc", "mpeg4", "mpeg2video", "vc1", "vp9", "av1"]
if settings("transcode_h265.bool"): if settings("transcode_h265.bool"):
codecs.remove("hevc") codecs.remove("hevc")
codecs.remove("h265")
if settings("transcode_mpeg2.bool"): if settings("transcode_mpeg2.bool"):
codecs.remove("mpeg2video") codecs.remove("mpeg2video")
@ -384,14 +383,12 @@ class PlayUtils(object):
return ",".join(codecs) return ",".join(codecs)
def get_transcoding_video_codec(self): def get_transcoding_video_codec(self):
codecs = ["h264", "hevc", "h265", "mpeg4", "mpeg2video", "vc1"] codecs = ["h264", "mpeg4", "mpeg2video", "vc1"]
if settings("transcode_h265.bool"): if settings("videoPreferredCodec") == "H265/HEVC":
codecs.remove("hevc") codecs.insert(0, "hevc")
codecs.remove("h265") elif not settings("transcode_h265.bool"):
else: codecs.insert(1, "hevc")
if settings("videoPreferredCodec") == "H265/HEVC":
codecs.insert(2, codecs.pop(codecs.index("h264")))
if settings("transcode_mpeg2.bool"): if settings("transcode_mpeg2.bool"):
codecs.remove("mpeg2video") codecs.remove("mpeg2video")
@ -480,7 +477,7 @@ class PlayUtils(object):
profile["CodecProfiles"].append( profile["CodecProfiles"].append(
{ {
"Type": "Video", "Type": "Video",
"codec": "h265,hevc", "codec": "hevc",
"Conditions": [ "Conditions": [
{ {
"Condition": "EqualsAny", "Condition": "EqualsAny",

View file

@ -0,0 +1,247 @@
import pytest
from jellyfin_kodi.helper import playutils
from jellyfin_kodi.helper.playutils import PlayUtils
@pytest.fixture
def play_utils():
class ApiClient:
class config:
data = {"auth.token": ""}
yield PlayUtils({}, api_client=ApiClient)
class PatchedSettings:
# TODO: move settings helper to separate file
settings = {}
def __init__(self):
self.clear()
@classmethod
def __call__(cls, setting: str, value=None):
if value is None:
result = cls.settings[setting.replace(".bool", "")]
if result and setting.endswith(".bool"):
result = result in ("true", "1", True)
return result
if setting.endswith(".bool"):
setting = setting.replace(".bool", "")
value = bool(value)
cls.settings[setting] = value
return None
@classmethod
def clear(cls):
cls.settings.clear()
# TODO: read defaults from settings.xml
cls.settings.setdefault("transcode_h265", False)
cls.settings.setdefault("videoPreferredCodec", "H264/AVC")
cls.settings.setdefault("transcode_mpeg2", False)
cls.settings.setdefault("transcode_vc1", False)
cls.settings.setdefault("audioPreferredCodec", "AAC")
cls.settings.setdefault("transcode_vp9", False)
cls.settings.setdefault("transcode_av1", False)
@pytest.fixture(autouse=True)
def mock_settings(monkeypatch):
patched = PatchedSettings()
monkeypatch.setattr(playutils, "settings", patched)
@pytest.mark.parametrize(
"transcode_h265, preferred_codec, expected_result",
[
(False, "H265/HEVC", "hevc,h264,mpeg4,mpeg2video,vc1"),
(True, "H265/HEVC", "hevc,h264,mpeg4,mpeg2video,vc1"),
(False, "H264/AVC", "h264,hevc,mpeg4,mpeg2video,vc1"),
(True, "H264/AVC", "h264,mpeg4,mpeg2video,vc1"),
],
)
def test_get_transcoding_video_codec_settings(
play_utils, transcode_h265, preferred_codec, expected_result
):
playutils.settings("transcode_h265", transcode_h265)
playutils.settings("videoPreferredCodec", preferred_codec)
result = play_utils.get_transcoding_video_codec()
assert result == expected_result
@pytest.mark.parametrize(
"transcode_mpeg2, transcode_vc1, expected_result",
[
(False, False, "h264,hevc,mpeg4,mpeg2video,vc1"),
(True, False, "h264,hevc,mpeg4,vc1"),
(False, True, "h264,hevc,mpeg4,mpeg2video"),
(True, True, "h264,hevc,mpeg4"),
],
)
def test_get_transcoding_video_codec_transcode_options(
play_utils, transcode_mpeg2, transcode_vc1, expected_result
):
playutils.settings("transcode_mpeg2", transcode_mpeg2)
playutils.settings("transcode_vc1", transcode_vc1)
result = play_utils.get_transcoding_video_codec()
assert result == expected_result
@pytest.mark.parametrize(
"preferred_codec, expected_first, expected_second",
[
("H265/HEVC", "hevc", "h264"),
("H264/AVC", "h264", "hevc"),
],
)
def test_get_transcoding_video_codec_order(
play_utils, preferred_codec, expected_first, expected_second
):
playutils.settings("videoPreferredCodec", preferred_codec)
result = play_utils.get_transcoding_video_codec().split(",")
assert result[0] == expected_first, result
assert result[1] == expected_second, result
@pytest.mark.parametrize(
"preferred_codec, transcode_h265",
[
("H265/HEVC", True),
("H265/HEVC", False),
("H264/AVC", True),
("H264/AVC", False),
],
)
def test_get_transcoding_video_codec_no_duplicates(
play_utils, preferred_codec, transcode_h265
):
playutils.settings("videoPreferredCodec", preferred_codec)
playutils.settings("transcode_h265", transcode_h265)
result = play_utils.get_transcoding_video_codec().split(",")
assert len(result) == len(set(result))
@pytest.mark.parametrize(
"transcode_h265, preferred_codec, transcode_mpeg2, transcode_vc1, expected_result",
[
(True, "H264/AVC", True, True, "h264,mpeg4"),
(False, "H265/HEVC", False, False, "hevc,h264,mpeg4,mpeg2video,vc1"),
],
)
def test_get_transcoding_video_codec_combined_settings(
play_utils,
transcode_h265,
preferred_codec,
transcode_mpeg2,
transcode_vc1,
expected_result,
):
playutils.settings("transcode_h265", transcode_h265)
playutils.settings("videoPreferredCodec", preferred_codec)
playutils.settings("transcode_mpeg2", transcode_mpeg2)
playutils.settings("transcode_vc1", transcode_vc1)
result = play_utils.get_transcoding_video_codec()
assert result == expected_result
@pytest.mark.parametrize(
"transcode_h265, expected_result",
[
(False, "h264,hevc,mpeg4,mpeg2video,vc1,vp9,av1"),
(True, "h264,mpeg4,mpeg2video,vc1,vp9,av1"),
],
)
def test_get_directplay_video_codec(play_utils, transcode_h265, expected_result):
playutils.settings("transcode_h265", transcode_h265)
result = play_utils.get_directplay_video_codec()
assert result == expected_result
@pytest.mark.parametrize(
"transcode_mpeg2, transcode_vc1, expected_result",
[
(False, False, "h264,hevc,mpeg4,mpeg2video,vc1,vp9,av1"),
(True, False, "h264,hevc,mpeg4,vc1,vp9,av1"),
(False, True, "h264,hevc,mpeg4,mpeg2video,vp9,av1"),
(True, True, "h264,hevc,mpeg4,vp9,av1"),
],
)
def test_get_directplay_video_codec_transcode_options(
play_utils, transcode_mpeg2, transcode_vc1, expected_result
):
playutils.settings("transcode_mpeg2", transcode_mpeg2)
playutils.settings("transcode_vc1", transcode_vc1)
result = play_utils.get_directplay_video_codec()
assert result == expected_result
def test_get_directplay_video_codec_no_duplicates(play_utils):
result = play_utils.get_directplay_video_codec().split()
assert len(result) == len(set(result))
@pytest.mark.parametrize(
"transcode_h265, transcode_mpeg2, transcode_vc1, transcode_vp9, transcode_av1, expected_result",
[
(True, True, True, True, True, "h264,mpeg4"),
(False, False, False, False, False, "h264,hevc,mpeg4,mpeg2video,vc1,vp9,av1"),
],
)
def test_get_directplay_video_codec_combined_settings(
play_utils,
transcode_h265,
transcode_mpeg2,
transcode_vc1,
transcode_vp9,
transcode_av1,
expected_result,
):
playutils.settings("transcode_h265", transcode_h265)
playutils.settings("transcode_mpeg2", transcode_mpeg2)
playutils.settings("transcode_vc1", transcode_vc1)
playutils.settings("transcode_vp9", transcode_vp9)
playutils.settings("transcode_av1", transcode_av1)
result = play_utils.get_directplay_video_codec()
assert result == expected_result
@pytest.mark.parametrize(
"preferred_codec, expected_result",
[
("", "aac,mp3,ac3,opus,flac,vorbis"),
("AAC", "aac,mp3,ac3,opus,flac,vorbis"),
("MP3", "mp3,aac,ac3,opus,flac,vorbis"),
("AC3", "ac3,aac,mp3,opus,flac,vorbis"),
("OPUS", "opus,aac,mp3,ac3,flac,vorbis"),
("FLAC", "flac,aac,mp3,ac3,opus,vorbis"),
("VORBIS", "vorbis,aac,mp3,ac3,opus,flac"),
("UNKNOWN", "aac,mp3,ac3,opus,flac,vorbis"),
],
)
def test_get_transcoding_audio_codec(play_utils, preferred_codec, expected_result):
playutils.settings("audioPreferredCodec", preferred_codec)
result = play_utils.get_transcoding_audio_codec()
assert result == expected_result
def test_get_transcoding_audio_codec_case_insensitive(play_utils):
playutils.settings("audioPreferredCodec", "aAc")
result = play_utils.get_transcoding_audio_codec()
assert result == "aac,mp3,ac3,opus,flac,vorbis"
def test_get_transcoding_audio_codec_no_duplicates(play_utils):
result = play_utils.get_transcoding_audio_codec().split(",")
assert len(result) == len(set(result))
def test_get_transcoding_audio_codec_preserve_order(play_utils):
playutils.settings("audioPreferredCodec", "")
result = play_utils.get_transcoding_audio_codec()
assert result == "aac,mp3,ac3,opus,flac,vorbis"