docs(plex): the transcode comments say the CPU-decode route
This commit is contained in:
@@ -227,16 +227,17 @@ class PlexItem(Base, TimestampMixin, UpdatedAtMixin):
|
|||||||
playable: Mapped[str | None] = mapped_column(String(12), index=True)
|
playable: Mapped[str | None] = mapped_column(String(12), index=True)
|
||||||
probed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
probed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||||
# Transcode HW-capability facet (the playback-class filter). NULL for non-transcode items; for a
|
# Transcode HW-capability facet (the playback-class filter). NULL for non-transcode items; for a
|
||||||
# `playable="transcode"` item: True = the prod iGPU can hardware-transcode it (codec is HW-decodable
|
# `playable="transcode"` item: True = the prod iGPU ENCODES it (a HW-decodable 8-bit SDR source, a
|
||||||
# AND the source is 8-bit SDR), False = software libx264 only (AV1/XviD, or a 10-bit/HDR source that
|
# true-HDR one through the OpenCL tonemap, or an 8-bit source in a codec the CPU decodes for it —
|
||||||
# breaks the VAAPI nv12 pipeline — see plex/stream.py). Derived by `plex.sync.transcode_hw_capable`:
|
# VC-1, XviD, DivX), False = software libx264 (AV1, or a 10-bit SDR source — see plex/stream.py).
|
||||||
# set codec-only at sync (bit depth unknown from the cheap listing), then refined by the bit-depth
|
# Derived by `plex.sync.transcode_hw_capable`: set codec-only at sync (bit depth unknown from the
|
||||||
# backfill (`_probe_media_batch`) which downgrades a 10-bit/HDR HEVC to False. The one filterable
|
# cheap listing), then refined by the bit-depth backfill (`_probe_media_batch`), which downgrades a
|
||||||
# home of the invariant `_want_hw`'s source gate computes at play time — kept in sync via the shared
|
# 10-bit SDR HEVC to False. The filterable twin of the rule `_select_encode` applies at play time;
|
||||||
# classifier so the catalog facet and the runtime encoder choice never disagree by codec.
|
# the two share their codec sets, not their code, so a change to one tree is a change to both.
|
||||||
transcode_hw: Mapped[bool | None] = mapped_column(Boolean, index=True)
|
transcode_hw: Mapped[bool | None] = mapped_column(Boolean, index=True)
|
||||||
# Video bit depth (8/10/12…) from the Plex media Stream, filled by the bit-depth backfill. NULL =
|
# Video bit depth (8/10/12…) from the Plex media Stream, filled by the bit-depth backfill. NULL =
|
||||||
# not yet probed. Surfaced on the info page (e.g. "HEVC · 10-bit") and feeds `transcode_hw`.
|
# not yet probed. Surfaced on the info page (e.g. "HEVC · 10-bit") and feeds `transcode_hw` (a
|
||||||
|
# 10-bit SDR source drops to software; an HDR one keeps the GPU through the tonemap).
|
||||||
video_bit_depth: Mapped[int | None] = mapped_column(Integer)
|
video_bit_depth: Mapped[int | None] = mapped_column(Integer)
|
||||||
# Watermark for the bit-depth backfill: when this item's media Stream was last probed for
|
# Watermark for the bit-depth backfill: when this item's media Stream was last probed for
|
||||||
# bit-depth/HDR. NULL = never (the backfill's work-list). Only HW-decodable transcode items are
|
# bit-depth/HDR. NULL = never (the backfill's work-list). Only HW-decodable transcode items are
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ Playback is from the LOCAL physical file. Browser-compatible files (playable="di
|
|||||||
raw with HTTP range requests. Everything else that is h264 video (playable="remux") is remuxed on
|
raw with HTTP range requests. Everything else that is h264 video (playable="remux") is remuxed on
|
||||||
the fly to HLS with **video stream-copy** (cheap, I/O-bound — no video re-encode) and audio to AAC
|
the fly to HLS with **video stream-copy** (cheap, I/O-bound — no video re-encode) and audio to AAC
|
||||||
when needed. Non-h264 video (playable="transcode": HEVC/VP9/AV1/MPEG-2/XviD…) needs a full re-encode
|
when needed. Non-h264 video (playable="transcode": HEVC/VP9/AV1/MPEG-2/XviD…) needs a full re-encode
|
||||||
to H.264: on the prod host that runs on the Intel iGPU via VAAPI (`h264_vaapi`, near-zero CPU), with
|
to H.264: on the prod host that is encoded on the Intel iGPU via VAAPI (`h264_vaapi`) — decoded there
|
||||||
software libx264 as the automatic fallback where the render node is absent or the source codec isn't
|
too when it can, else decoded on the CPU and uploaded (VC-1, XviD, DivX) — with software libx264 as
|
||||||
hardware-decodable. Same HLS session/seek machinery either way — only the video encoder differs.
|
the fallback where the render node is absent, for a source neither GPU route takes (AV1, 10-bit SDR),
|
||||||
|
and when a GPU session produces nothing. Same HLS session/seek machinery either way.
|
||||||
|
|
||||||
Seek model (Jellyfin-style): one ffmpeg session per item, started at a given offset via `-ss`
|
Seek model (Jellyfin-style): one ffmpeg session per item, started at a given offset via `-ss`
|
||||||
(fast keyframe seek). A seek beyond the generated region restarts the session at the new offset,
|
(fast keyframe seek). A seek beyond the generated region restarts the session at the new offset,
|
||||||
@@ -501,16 +502,17 @@ def _want_hw(db: DbSession, item: PlexItem, transcode: bool, src: Path) -> bool:
|
|||||||
return _select_encode(db, item, transcode, src)[0]
|
return _select_encode(db, item, transcode, src)[0]
|
||||||
|
|
||||||
|
|
||||||
# CPU-nice increment for the SOFTWARE transcode child. The Plex ffmpeg is a Popen in the long-lived API
|
# CPU-nice increment for a transcode child that works the CPU: a software encode, or a CPU decode
|
||||||
# process (which must NOT itself be niced), so — unlike the download worker's _deprioritize (run once in
|
# feeding the GPU encoder. The Plex ffmpeg is a Popen in the long-lived API process (which must NOT
|
||||||
# its own main()) — we lower priority per-child via preexec_fn in the fork. The GPU path needs none of
|
# itself be niced), so — unlike the download worker's _deprioritize (run once in its own main()) — we
|
||||||
# this. Reuses the download child's nice default so all co-located re-encoders share one handicap.
|
# lower priority per-child via preexec_fn in the fork. The full-GPU path needs none of this. Reuses
|
||||||
|
# the download child's nice default so all co-located re-encoders share one handicap.
|
||||||
_SW_TRANSCODE_NICE = settings.download_child_nice
|
_SW_TRANSCODE_NICE = settings.download_child_nice
|
||||||
|
|
||||||
|
|
||||||
def _sw_child_preexec() -> None:
|
def _sw_child_preexec() -> None:
|
||||||
"""Runs in the forked child (POSIX) before exec: renice this software-transcode ffmpeg down so it
|
"""Runs in the forked child (POSIX) before exec: renice this CPU-bound transcode ffmpeg down so
|
||||||
can't starve the co-located API / Postgres / playback. Best-effort — a failed renice must not fail
|
it can't starve the co-located API / Postgres / playback. Best-effort — a failed renice must not fail
|
||||||
playback."""
|
playback."""
|
||||||
try:
|
try:
|
||||||
os.nice(_SW_TRANSCODE_NICE)
|
os.nice(_SW_TRANSCODE_NICE)
|
||||||
|
|||||||
Reference in New Issue
Block a user