4K60 Transcode Ceiling and VBR Peaks on Wi-Fi Clients¶
Why a healthy Plex server with working hardware transcoding still can't serve 4K60 content to a Wi-Fi-only client — and why "enable hardware acceleration" is not the answer.
This case contradicts the usual advice
The standard fix for Apple TV playback problems is "set Quality to Play Original so it direct plays." That does not help when the source file's peak bitrate exceeds what the Wi-Fi link can carry. Direct play and transcode both fail, for different reasons.
Symptom¶
On an Apple TV 4K (Wi-Fi only, no Ethernet port):
- 4K content plays but is choppy, worse during high-motion scenes
- Eventually it stops playing entirely
- Plex shows "Your server is not powerful enough to convert video"
- The Plex dashboard reports playback at an implausibly high bitrate (e.g. 121 Mbps)
1080p content on the same server, client, and network plays perfectly.
Root cause: two independent problems¶
1. The source files have unconstrained VBR peaks¶
ffprobe reports an average bitrate, which hides the real problem. Measure the
per-second bitrate instead:
F="/plex/plex/yourfile.mp4"
ffprobe -v error -select_streams v:0 -show_entries packet=pts_time,size \
-of csv=p=0 "$F" \
| awk -F, '{s=int($1); bytes[s]+=$2} END {for (t in bytes) print bytes[t]*8/1000000}' \
| sort -rn > /tmp/br.txt
echo "peak: $(head -1 /tmp/br.txt)"
echo "p95: $(awk '{a[NR]=$1} END {print a[int(NR*0.05)]}' /tmp/br.txt)"
echo "median: $(awk '{a[NR]=$1} END {print a[int(NR/2)]}' /tmp/br.txt)"
A real example from this library:
| Metric | Bitrate |
|---|---|
Average (what ffprobe reports) |
32 Mbps |
| Median | 24 Mbps |
| 95th percentile | 92 Mbps |
| Peak | 149 Mbps |
No Wi-Fi link sustains 92–149 Mbps bursts on a contended 5 GHz radio. The file stutters exactly when the bitrate spikes — i.e. during high-motion scenes.
Cause: -rc_mode CQP with no rate ceiling
These files came from a batch re-encode using -c:v hevc_vaapi -rc_mode CQP -qp 28.
Constant-QP mode spends whatever bits a frame needs with no upper bound. The result
is a modest average and enormous peaks. Always pair an encode with -maxrate and
-bufsize if the output will be streamed over a constrained link.
2. The GPU cannot transcode 4K60 HEVC in real time¶
When the client requests a lower quality, Plex must transcode. Measured on an RX 480
(Polaris, hevc_vaapi), decoding 4K60 HEVC and re-encoding:
| Pipeline | Speed |
|---|---|
| 4K60 HEVC decode → 4K HEVC encode | 0.3–0.4x |
| 4K60 HEVC decode → 1080p H.264 encode | 1.1–1.5x |
Real-time playback needs speed ≥ 1.0, and roughly 2x+ to build and hold a client-side
buffer. Even the 1080p output path only just clears 1.0, because the 4K60 HEVC decode
dominates the cost and no output setting reduces it.
Benchmark gotcha
A decode-only test measured slower (1.19x) than decode+encode (1.49x). The decode-only
run omitted -hwaccel_output_format vaapi, forcing an expensive GPU→CPU frame readback.
Always keep frames on the GPU when benchmarking VAAPI, or you measure PCIe transfer
rather than the decoder.
The livelock¶
These two problems combine into a self-sustaining failure:
- Transcode runs at ~1.1x — barely above real time
- Client can't accumulate a startup buffer, times out, and retries
- Each retry spawns a new transcode session
- Stacked sessions contend for the same GPU, making every one slower
- → more timeouts → more sessions
Symptoms in the log: multiple session UUIDs within a few minutes, each ending in
exit code ... is -9 (signal: Killed), and zero .ts segments produced.
L="/plex/plexdata/Library/Application Support/Plex Media Server/Logs/Plex Media Server.log"
# Live transcode speed — the number that matters
sudo grep -ao "speed=[0-9.]*" "$L" | tail -10
# Session churn
sudo grep -a "signal: Killed" "$L" | tail
# Are segments actually being written?
D="/plex/plexdata/Library/Application Support/Plex Media Server/Cache/Transcode/Sessions"
sudo find "$D" -name '*.ts' -newermt '-60 seconds' | wc -l
SIGKILL here is a symptom, not a cause
Confirm it is not the OOM killer before chasing memory. If dmesg/journalctl show no
OOM events and there are no systemd MemoryMax limits, the SIGKILLs are Plex cleaning up
sessions the client already abandoned.
Fix¶
For a Wi-Fi-only client and 4K60 sources, transcoding is not viable on this hardware. Re-encode the sources to 1080p ahead of time so the client direct plays:
ffmpeg -y \
-hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi \
-i input.mp4 \
-vf 'scale_vaapi=w=1920:h=1080:format=nv12' \
-c:v hevc_vaapi -rc_mode VBR -b:v 12M -maxrate 20M -bufsize 40M \
-c:a copy \
-movflags +faststart \
output.mp4
Key points:
-rc_mode VBRwith-maxrate/-bufsizebounds the peak. This is the part that makes it Wi-Fi-deliverable; the average was never the problem.-c:a copypasses the audio through untouched rather than re-encoding it (Plex's transcode was converting AC3 → Opus). Check your own source first: on this library the audio was already AC3 stereo 320k, so Plex'sochl='stereo'downmix was a no-op — worth verifying withffprobe -select_streams abefore assuming surround is being lost.- Encoding to the same path preserves Plex's
added_at(see HEVC VAAPI Batch Encode).
Automated in reencode_4k_1080p.sh on majorhome (queue/resume, verify-before-swap,
free-space guard).
What does not fix it¶
| Attempted | Result |
|---|---|
| Set client Quality to "Play Original" | Avoids transcode, but 149 Mbps peaks still exceed Wi-Fi |
| Enable hardware acceleration | Already enabled and working correctly — still only 0.3x at 4K→4K |
| Force 1080p output resolution | Improves 0.3x → 1.1x, still too marginal to hold a buffer |
| Lower transcode quality further | Minimal gain — the 4K60 decode is the bottleneck, not the encode |
| Ethernet | Would work (gigabit carries 149 Mbps easily) — but Apple TV 4K Wi-Fi models have no port |