fix(plex): a codec the GPU cannot decode is decoded on the CPU and still encoded on the GPU
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.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""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'
|
||||
"""
|
||||
)
|
||||
Reference in New Issue
Block a user