The prod iGPU has no VC-1 Advanced decode ("No support for codec vc1 profile 3"), so every VC-1
session died on the GPU and fell back to libx264, which needs ~17 s for the three warm-up segments:
a start or a seek took ~19.5 s. VC-1, XviD and DivX now take CPU decode -> hwupload -> h264_vaapi
(three segments in 5.5 s for a 1080p VC-1 remux, measured on prod hardware with this argv).
The transcode_hw facet now says whether the GPU encodes: VC-1/XviD/DivX count, and so does an HDR
film (the OpenCL tonemap runs on the GPU; the facet said software). Migration 0119 rewrites the
stored facet and re-queues 10-bit items for the HDR probe.
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""plex_items.transcode_hw: the facet says whether the GPU ENCODES, under the measured codec sets
|
|
|
|
Measured on prod (2026-11-07): the iGPU has no VC-1 Advanced decode, so a VC-1 film never ran on the
|
|
GPU although the facet said it did; VC-1, XviD and DivX now take the CPU-decode → GPU-encode path; and an
|
|
HDR film runs on the GPU through the OpenCL tonemap although the facet said software. The rule is
|
|
`plex.sync.transcode_hw_capable`; the two codec sets below are pinned to `plex.sync` by
|
|
tests/test_plex_playback_class.py.
|
|
|
|
Rewritten from the stored codec and bit depth. HDR is not stored, so a 10-bit HW-decodable item is set to
|
|
software here and its probe watermark cleared: the bit-depth backfill (`plex.sync._probe_media_batch`,
|
|
a bounded batch per Plex sync) reads its HDR flag again and moves an HDR one back to the GPU.
|
|
"""
|
|
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0119_plex_transcode_gpu"
|
|
down_revision: Union[str, None] = "0118_videos_shuffle_key"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
HW_DECODABLE_CODECS = ("h264", "hevc", "h265", "vp9", "mpeg2video")
|
|
CPU_DECODE_CODECS = ("vc1", "mpeg4", "msmpeg4v3")
|
|
|
|
|
|
def _in(codecs: tuple[str, ...]) -> str:
|
|
return ", ".join(f"'{c}'" for c in codecs)
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
f"""
|
|
UPDATE plex_items
|
|
SET transcode_hw = CASE
|
|
WHEN lower(codec_video) IN ({_in(HW_DECODABLE_CODECS)})
|
|
THEN coalesce(video_bit_depth, 8) <= 8
|
|
WHEN lower(codec_video) IN ({_in(CPU_DECODE_CODECS)})
|
|
THEN coalesce(video_bit_depth, 8) <= 8
|
|
ELSE false
|
|
END
|
|
WHERE playable = 'transcode'
|
|
"""
|
|
)
|
|
op.execute(
|
|
f"""
|
|
UPDATE plex_items
|
|
SET media_probed_at = NULL
|
|
WHERE playable = 'transcode'
|
|
AND lower(codec_video) IN ({_in(HW_DECODABLE_CODECS)})
|
|
AND video_bit_depth > 8
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
# The old rule: a decodable codec (VC-1 counted) at 8 bits; HDR and every other codec software.
|
|
op.execute(
|
|
f"""
|
|
UPDATE plex_items
|
|
SET transcode_hw = lower(codec_video) IN ({_in(HW_DECODABLE_CODECS + ("vc1",))})
|
|
AND coalesce(video_bit_depth, 8) <= 8
|
|
WHERE playable = 'transcode'
|
|
"""
|
|
)
|