Homelab Academy / Modules
Guided practice + break/fix + lab gate.
Module: Requests & Media Servers
Activity type: Lab (Practice)
Lab risk: medium
Objective: Distinguish hardware decoding, hardware filtering, hardware encoding, and software fallback.
Every file or directory created, changed, or removed by this lab must remain under /opt/lab-classroom/class60/. Package installation, driver changes, service configuration, and permission changes are outside the lab scope.
1
Initialize the lab workspace
Confirm that /opt/lab-classroom/class60/ was pre-provisioned for your account.
Run: mkdir -p /opt/lab-classroom/class60
Run: printf '%s\n' "Class 60 hardware transcoding evidence" > /opt/lab-classroom/class60/README.txt
Run: ffmpeg -version > /opt/lab-classroom/class60/ffmpeg-version.txt 2>&1
2
Discover available hardware interfaces
Run: ffmpeg -hide_banner -hwaccels 2>&1 | tee /opt/lab-classroom/class60/hwaccels.txt
Run: ffmpeg -hide_banner -encoders 2>&1 | grep -E 'h264_(vaapi|nvenc|qsv)|hevc_(vaapi|nvenc|qsv)' | tee /opt/lab-classroom/class60/hardware-encoders.txt
For a VA-API host, run: ls -l /dev/dri 2>&1 | tee /opt/lab-classroom/class60/dri-devices.txt
For an NVIDIA host, run: nvidia-smi 2>&1 | tee /opt/lab-classroom/class60/nvidia-status.txt
Select exactly one hardware branch that is listed by FFmpeg and supported by the host.
3
Generate a deterministic source clip
Run: ffmpeg -hide_banner -y -f lavfi -i testsrc2=size=1280x720:rate=30 -f lavfi -i sine=frequency=1000:sample_rate=48000 -t 10 -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k /opt/lab-classroom/class60/source.mp4 > /opt/lab-classroom/class60/source-generation.log 2>&1
Run: ffprobe -v error -show_entries stream=index,codec_type,codec_name,width,height,pix_fmt -show_entries format=duration -of default=noprint_wrappers=1 /opt/lab-classroom/class60/source.mp4 | tee /opt/lab-classroom/class60/source-probe.txt
4
Create a software baseline
Run: ffmpeg -hide_banner -y -benchmark -i /opt/lab-classroom/class60/source.mp4 -vf scale=854:480 -c:v libx264 -preset medium -b:v 2M -c:a copy /opt/lab-classroom/class60/software-output.mp4 > /opt/lab-classroom/class60/software-transcode.log 2>&1
Treat the recorded timing as an observation for this host and run only, not as a general benchmark.
5
Run the VA-API branch if selected
Confirm that vaapi appears in hwaccels.txt, h264_vaapi appears in hardware-encoders.txt, and the selected render node exists.
Run: ffmpeg -hide_banner -y -benchmark -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi -i /opt/lab-classroom/class60/source.mp4 -vf 'scale_vaapi=w=854:h=480:format=nv12' -c:v h264_vaapi -b:v 2M -c:a copy /opt/lab-classroom/class60/hardware-output.mp4 > /opt/lab-classroom/class60/hardware-transcode.log 2>&1
If the render node differs, substitute the correct existing node without changing device permissions.
6
Run the NVIDIA branch if selected
Confirm that cuda appears in hwaccels.txt, h264_nvenc appears in hardware-encoders.txt, and nvidia-smi reports a working device.
Run: ffmpeg -hide_banner -y -benchmark -hwaccel cuda -hwaccel_output_format cuda -i /opt/lab-classroom/class60/source.mp4 -vf 'scale_cuda=854:480' -c:v h264_nvenc -b:v 2M -c:a copy /opt/lab-classroom/class60/hardware-output.mp4 > /opt/lab-classroom/class60/hardware-transcode.log 2>&1
7
Validate the hardware output
Run only after the selected hardware command exits successfully.
Run: ffprobe -v error -show_entries stream=index,codec_type,codec_name,width,height,pix_fmt -show_entries format=duration -of default=noprint_wrappers=1 /opt/lab-classroom/class60/hardware-output.mp4 | tee /opt/lab-classroom/class60/hardware-probe.txt
Run: ffmpeg -hide_banner -v error -i /opt/lab-classroom/class60/hardware-output.mp4 -map 0:v:0 -f null - > /opt/lab-classroom/class60/hardware-decode-check.log 2>&1
Run: grep -E 'h264_vaapi|h264_nvenc|Stream mapping|frame=|bench:' /opt/lab-classroom/class60/hardware-transcode.log > /opt/lab-classroom/class60/hardware-evidence.txt
Run: sha256sum /opt/lab-classroom/class60/source.mp4 /opt/lab-classroom/class60/software-output.mp4 /opt/lab-classroom/class60/hardware-output.mp4 > /opt/lab-classroom/class60/checksums.sha256
8
Record the conclusion
Create /opt/lab-classroom/class60/conclusion.txt and state the selected backend, device, FFmpeg version, output properties, whether the complete decode check was clean, and which log lines demonstrate the hardware encoder.
Do not claim that the entire pipeline was hardware accelerated unless the command and log demonstrate hardware decoding, hardware-compatible filtering, and hardware encoding.
Do not claim a universal speed or quality advantage from this single synthetic clip.
| Symptom | Likely cause | Fix |
|---|---|---|
| FFmpeg reports that the requested encoder is unknown. | The installed FFmpeg build was compiled without that encoder, or the encoder name was typed incorrectly. | Review hardware-encoders.txt and select only an encoder listed by the active FFmpeg binary. Rebuilding or replacing FFmpeg is outside this lab and must follow the host's normal change process. |
| The VA-API command reports that it cannot open /dev/dri/renderD128. | The render node is absent, a different node is in use, the driver did not initialize, or the current account lacks device access. | Inspect dri-devices.txt, identify the correct render node, and verify account membership in the host's GPU-access group. Do not broaden device permissions as a shortcut. |
| The VA-API command fails with an unsupported profile, entry point, or surface format. | The driver or GPU does not support the requested codec operation, profile, dimensions, or pixel format. | Confirm device capabilities with host-supported VA-API diagnostic tooling, retain NV12 where required, and test a codec and profile actually supported by the hardware. |
| The filter graph reports that software and hardware pixel formats cannot be connected. | Frames are crossing between system memory and hardware memory without a compatible upload, download, or hardware-native filter. | Use the backend-native scaling filter shown in the lab, or explicitly design the required transfer with the appropriate hardware frame filters. Recheck the log after every filter-graph change. |
| The NVIDIA command reports that no capable devices were found. | The driver is unavailable, the device is not exposed to the environment, the GPU lacks the requested NVENC generation, or the FFmpeg build and driver are incompatible. | Confirm that nvidia-smi succeeds, that the GPU model supports the requested codec, and that the application environment can see the device. |
| The command succeeds, but GPU telemetry shows little or no sampled utilization. | The clip is too short, sampling missed the operation, the selected metric monitors a different engine, or only one pipeline stage used hardware. | Use the saved FFmpeg log as primary evidence, select a telemetry view for the video engine, and if policy permits, repeat the same lab command with a longer source generated inside the lab directory. |
| The output file exists but the decode validation reports errors. | The transcode was interrupted, the muxer did not finalize, the driver produced an invalid stream, or storage was exhausted. | Inspect the end of hardware-transcode.log, verify available storage through read-only inspection, remove only the failed lab output through the documented rollback process, and rerun the selected branch. |
| The hardware run is not faster than the software baseline. | Startup overhead, transfers between memory domains, a short synthetic source, different encoder behavior, filtering, or host contention dominates the test. | Do not classify the run as failed based on speed alone. Verify backend use and output correctness, then evaluate representative production media under a separately approved test plan. |
| Subtitles or tone mapping in a media server cause CPU use even though this lab succeeds. | The production filter chain includes operations unsupported by the selected hardware backend or requires frame transfers. | Reproduce the application's exact codec, subtitle, scaling, and tone-mapping path and inspect its complete FFmpeg command and logs. |
Grant access only to the render or video device required by the media workload.
Prefer render nodes over interfaces that unnecessarily expose display-control capabilities.
Use normal GPU-access group membership or an orchestrator's explicit device assignment rather than globally writable device nodes.
Treat media files as untrusted input because malformed codecs and containers exercise complex parsers and drivers.
Keep the kernel, GPU driver, FFmpeg, and media application within supported security maintenance windows.
Avoid privileged containers for transcoding; map only the required GPU device and retain the smallest practical capability set.
Do not expose vendor management interfaces to unrelated workloads merely to enable video encoding.
Preserve logs without including private filenames, user paths, media titles, or tokens when sharing troubleshooting evidence.
A successful host-side test proves that the host stack can work, but it does not prove that a container has the correct device mapping, supplemental groups, driver libraries, or FFmpeg build. Verify those layers independently.
This lab uses generated synthetic media so that evidence files can be shared without disclosing personal content.
Rollback removes only artifacts created under the dedicated class directory. It does not change drivers, packages, users, groups, services, or device configuration.
Run: find /opt/lab-classroom/class60 -mindepth 1 -maxdepth 1 -printf '%f\n'
find /opt/lab-classroom/class60 -mindepth 1 -maxdepth 1 -delete
Run: find /opt/lab-classroom/class60 -mindepth 1 -maxdepth 1 -print
The postcheck prints nothing and the class60 directory itself still exists.