以往安裝 JellyFin 就是在DSM裡面的 Container Manager 安裝 JellyFin 的 Docker image,然後將DSM上的影片路徑映射到 Docker 之中,如此就可以透過 JellyFin 來管理影片。不過透過 DSM 來執行 JellyFin 的缺點就是 NAS 的 CPU 實在過於貧弱,通常也沒有內建獨立顯卡晶片可以用來硬體解碼影片編碼來進行串流播放,所以一旦遠端裝置無法即時解碼裝置不支援的影片編碼(例如:HEVC 格式),如果想即時轉檔解碼為裝置支援的影片編碼,就只能透過主機的 CPU 來進行軟體解碼,再依據裝置支援的編碼格式進行重新編碼(簡稱軟解)。讓 NAS 貧弱的 CPU 來做軟解這件事只能說是力不從心,所以在 DSM 上 JellyFin 就只能放棄即時解碼串流播放這件事。
最近透過淘寶買了一台討論度很高的 NAS 硬體機-天鋇科技的WTR MAX,除了有 6 個 3.5 吋硬碟盤位,還具備 5 個 M.2 NVME 固態硬碟盤位,採用 AMD Ryzen 7 Pro 8845HS 作為處理器,同時內建 Radeon 780M 顯示晶片,TDP 為 35W~45W,算是效能高且功耗適中的處理器,還具備 Oculink 擴充外接埠可作為未來擴充 PCIE 介面的能力,強大的硬體規格實在令人忍不住要買回來作為 PVE 虛擬伺服器試試。
(圖片取自天鋇科技官網)
在 WTR MAX 上安裝 PVE 並沒什麼困難,但是要做到把硬體裝置直通給虛擬機,還是費了一點功夫,下次有機會再來寫一下如何在 AMD 的裝置上設定 PVE 的硬體直通設定。今天的重點是要來記錄如何在 PVE 上透過 JellyFin 以及裝置上的核顯晶片來做影片硬解。思考了一下,比較簡單的做法還是把影片檔案放在原來的 NAS 中,但是將 JellyFin 的影像檔安裝並啟動於 PVE LXC 容器中,接著透過 SMB 檔案分享(NAS 的強項)將影片分享給 LXC JellyFin 容器來做影片資料分析,以及影片串流播放。接下來就一步一步記錄下來。
- 建立LXC容器,同時啟動 JellyFin 容器
- 在 PVE 上建立 LXC 容器,記得取消「無特權容器」的預設勾選,使用特權容器比較不會遇到權限上的問題。

- 選擇要使用的 LXC 容器範本

- 設定容器使用的磁碟空間容量,這裡我設定 64GB 是因為 JellyFin 會將刮削回來的影片資訊及海報存放,如果有特別將儲存的目錄硬射到其他儲存空間,那就可以設定小一點的空間。

- 設定容器使用的處理器數量

- 設定容器使用的記憶體大小

- 設定容器使用的網路設定

- 調整 LXC 容器的選項設定
在 PVE 9.1 中,Docker 需要更完整的容器嵌套(Nesting)與系統調用權限。
從 LXC 容器中找到選項 –>功能,點擊編輯,務必勾選以下兩項:
Nesting (嵌套:這是 Docker 運行的核心需求)
FUSE (有時對某些檔案系統掛載有幫助)

建立完畢,這樣就可以直接啟動LXC容器,確認執行環境是否一切正常。
- 將顯卡裝置及外部的影片目錄映射進入LXC容器中
- 先確認PVE主機有正確安裝顯卡驅動程式
WTR MAX 的核顯是 Radeon 780M,如果有正確安裝驅動程式,在 /dev/dri 目錄下應該會有 card<序號> 和 renderD128 這兩個裝置。使用 ls -l /dev/dri 指令來檢查。另外 AMD 顯卡與 Intel 不同,它還需要映射另外一個裝置 /dev/kfd 到 LXC 容器中,可以使用 ls -l /dev/kfd 指令來確認。

- 修改 LXC 容器的啟動設定
修改/etc/pve/lxc/<容器編號>.conf,將下列參數加到設定檔最後,設定完畢後重啟 LXC 容器。
(注意:510:0 是 /dev/kfd 的常見編號,請用 ls -l /dev/kfd 確認第一個數字,card1/renderD128也是用同樣方式確認編號)
lxc.cgroup2.devices.allow: c 226:1 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.cgroup2.devices.allow: c 510:0 rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry: /dev/kfd dev/kfd none bind,optional,create=file
- 在 LXC 容器中安裝 docker /docker-compose 套件
進入 LXC 後,執行標準安裝
apt update && apt install docker.io docker-compose -y
- 在 LXC 容器中編寫 docker-compose.yml
在/root下建立 jellyfin 目錄,並建立兩個子目錄 cache / config,用來存放 jellyfin 運行過程中產生的資料,另外再建立一個 movie 用來映射從PVE主機映射進來的網路芳鄰影片資料。目錄結構如下
/root
|-- movie
|-- jellyfin
| |-- cache
| |-- config
| -- docker-compose.yml
編輯 docker-compose.yml
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
ports:
- "8096:8096"
user: 0:0
group_add:
- "44"
- "128"
devices:
- /dev/dri:/dev/dri
- /dev/kfd:/dev/kfd
volumes:
- /root/jellyfin/config:/config
- /root/jellyfin/cache:/cache
- /root/movie:/media:ro
environment:
- TZ=Asia/Taipei
- JELLYFIN_PublishedServerUrl=這裡定義外界訪問此 Jellyfin 伺服器的公網 URL,例:http://xxx.xxx.xx
restart: always
執行 docker-compose up -d 用以安裝並啟動 JellyFin 容器。
docker-compose up -d
最後設定 Docker 服務在 LXC 容器重啟時也能自動啟動,設定好就重啟 LXC 容器。
systemctl enable docker
systemctl enable containerd
- 將影片samba分享目錄映射到 LXC 容器中
我的做法是在 PVE 主機將遠端的 DSM 分享出來的影片目錄掛載起來,再映射到 LXC 容器內,當然也有人會覺得為何不直接在 LXC 容器中掛載遠端目錄,因為 PVE 的安全防護機制(AppArmor)在 LXC 容器中,預設是不允許直接執行 mount 指令(尤其是 CIFS/SMB),因為這被視為潛在的安全風險。
編輯 PVE 主機的 /etc/fstab,增加下列一行
//<遠端DSM IP>/Movie <PVE主機本地路徑> cifs credentials=/etc/smb_creds,iocharset=utf8,x-systemd.automount,x-systemd.idle-timeout=60,x-systemd.mount-timeout=30,_netdev,noauto,x-systemd.after=network-online.target 0 0
//<遠端DSM IP>/Movie 為遠端分享的IP及路徑
<PVE主機本地路徑>為本地掛載的路徑,像我的是 /mnt/pve/movie
因為無法確定遠端分享資料夾的運作狀態,為了避免因為 /etc/fstab 中寫死掛載遠端路徑,若遠端路徑掛載失敗,而導致 PVE 主機啟動失敗,所以在 /etc/fstab 中改為利用 systemd 的 automount 功能。這會讓系統在「真正存取資料」時才去觸發掛載動作,而不是在開機瞬間就去掛載遠端路徑。x-systemd.automount 參數設定只有在存取目錄時才掛載,_netdev 參數則設定網路通了再進行掛載。
另外為了避免網路分享的帳號密碼寫死在 /etc/fstab 而導致帳密曝露,透過 credentials 參數來指定將帳號密碼放在 /etc/smb_creds 檔案,切記 /etc/smb_creds 權限必須控制好只有 root 能讀取。檔案內容如下格式
username=你的帳號
password=你的密碼
domain=WORKGROUP
重開 PVE 主機,並確認遠端目錄已經在本地掛載成功後,接著在 PVE 主機設定目錄穿透映射,執行指令如下
pct set <LXC容器編號> -mp0 <PVE主機本地路徑>,mp=<LXC容器內的影片路徑>
<PVE主機本地路徑>為本地掛載的路徑,像我的是 /mnt/pve/movie
<LXC容器內的影片路徑>是LXC容器中給JellyFin讀取的影片目錄,像我是放在 /root/movie
- 設定 JellyFin 的硬體解碼加速設定
從JellyFin管理介面進入,選擇 控制台 –> 播放–>轉檔,硬體加速選擇 「Video Acceleration API (VAAPI)」,VA-API裝置請設定為「/dev/dri/renderD128」,啟用硬體解碼的項目就請依照顯卡晶片的能力來進行設定,因為 Radeon 780M 顯示晶片效能強大,所以我就全啟用了。

如此就完成了JellyFin 的影片硬解設定。
- 如何確認是否有正確透過硬體來解碼並重新編碼?
- 在 PVE 主機安裝 radeontop 套件
安裝 radeontop 套件,並執行 radeontop 指令,可以類似 top 指令一樣即時觀察顯示晶片的運作情形
apt install radeontop
radeontop
- 試著播放一個影片並故意手動調低畫質來觸發轉碼

- 檢查JellyFin的播放模式
影片播放時,從JellyFin右下角的齒輪圖示點選播放內容,如果紅框部分顯示「轉碼」,那就表示目前已經確實觸發影片轉碼。如果顯示「直接播放」,那就是沒有觸發。

執行 radeontop 指令確認是否有使用硬體解碼影片
如果 Graphics pipe / Shader Interpolator / Clip Rectangle 的數值有持續波動,代表有正確使用顯示晶片來硬體解碼影片
反之如果上述幾個欄位都顯示為 0.00% 無波動情形,那就代表還是使用 CPU 來進行軟體解碼播放