Укращаем запал Plex на ARM пристроях

Все почалося в мого питання Toster. І ось вже півроку як я використовую медіа сервер Plex. Для тих, хто про нього не чув, поясню: це ПО, яке аналізує і структурує вашу музичну бібліотеку, і надає доступ до неї через web і не тільки, такий собі персональний Netflix без реєстрації і SMS. Я використовую Plex для перегляду фільмів і серіалів через браузер на ноутбуці або Chromebook.
image
Раніше мені доводилося налаштовувати NFS або Samba share, чаклувати з automount(8), миритися з отваливанием share після suspend-resume, або просто копіювати файли по sftp/scp, але тепер я використовую Тайд Plex. На жаль, з ним теж не все просто.
Роль мого домашнього сервера виконує Cubietruck з процесором ARM Cortex-A7 1GHz і дистрибутивом Armbian (Vanilla kernel для підтримки Docker і namespaces(7)). Його цілком вистачає для повсякденних потреб (зберігання бекапів, VPN сервер), але він очевидно не призначений для більш ресурсоємних речей.
Plex є freeware software. Він безкоштовний, але не вільний, що накладає певні обмеження. Наприклад, немає вихідних кодів і офіційних deb-пакетів під будь-яку архітектуру процесора. Ну і само собою багатьом параноїком-кіноманам не захочеться встановлювати кота в мішку на свою систему.
Emby Media ServerІснує opensource проект Emby Media Server, аналог Plex, написаний на Mono. На жаль, у нього є проблеми з відтворенням файлів в браузерах. Багато відео форматів Emby транскодирует повністю, навіть якщо спочатку використовується кодек h264.
На сьогоднішній день Plex дозволяє відтворити більше форматів відео без повної перекодування, ніж Emby Media Server. Можливо хтось з вас допоможе виправити цю ситуацію. А поки можна поворожити над файлом browserdeviceprofile.js, який відповідає за профілі браузерів.
Першу проблему ми вирішимо з допомогою офіційно розповсюджуються пакетів для NAS пристроїв, а другу частково з допомогою Docker.
За основу візьмемо пакет для NAS QNAP з архітектурою ARMv7-X31+ (цей білд підтримує розширення Neon, яке підтримується Cubietruck'ом, перевірити можна командою
cat /proc/cpuinfo | grep neon
):
$ curl -s https://plex.tv/api/downloads/1.json | python -mjson.tool | grep x31plus
"url": "https://downloads.plex.tv/plex-media-server/1.0.3.2461-35f0caa/PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg

Файл qpkg є симбіозом shell скрипта і кількох архівів. Розпакувати його в теку
plex_media_server
ми можемо з допомогою команди:
$ mkdir plex_media_server
$ wget https://downloads.plex.tv/plex-media-server/1.0.3.2461-35f0caa/PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg
$ dd if=PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg bs=22954 skip=1 status=none | tar -xzf - -C plex_media_server

Отримані файли можна помістити в Docker контейнер, але про це трохи пізніше. Припустимо, що ми запустили Plex і збираємося подивитися в браузері фільм, який вже закодований в h264 зі звуковою AC3 5.1. Що зробить Plex? Він почне перекодувати доріжку AC3 5.1 в AAC 5.1. А для перегляду відео на ноутбуці нам немає необхідності слухати відео з шістьма каналами, так і нешвидкий процесор дає про себе знати з періодичними паузами при перегляді.
На щастя, у Plex є конфігураційні профілі, які можна редагувати. Наприклад профіль для браузерів
Resources/Profiles/Web.xml
.
Web.xml
<?xml version="1.0" encoding="utf-8"?>
<Client name="Web">
<!-- Author: Plex Inc. -->
<TranscodeTargets>
<VideoProfile protocol="hls" container="mpegts" codec="h264" audioCodec="aac,mp3" context="streaming" />
<VideoProfile protocol="dash" container="mp4" codec="h264" audioCodec="aac" context="streaming" />
<VideoProfile protocol="http" container="mkv" codec="h264" audioCodec="aac,mp3" context="streaming" />
<MusicProfile container="mp3" codec="mp3" />
<PhotoProfile container="jpeg" />
<SubtitleProfile container="ass" codec="ass" context="all" />
</TranscodeTargets>
<CodecProfiles>
<VideoCodec name=".*">
<Limitations>
<UpperBound name="video.bitDepth" value="8" />
</Limitations>
</VideoCodec>
<VideoAudioCodec name=".*">
<Limitations>
<UpperBound name="audio.channels" value="6" />
</Limitations>
</VideoAudioCodec>
</CodecProfiles>
</Client>

У ньому ми бачимо параметр
<UpperBound name="audio.channels" value="6" />
, який говорить про те, що максимальна кількість каналів для аудіо не повинно перевищувати шість. А при перекодуванні аудіо доріжки це означає, що якщо ми перетворимо AC3 6 каналів в AAC, то результуючий AAC теж буде мати 6 каналів, тобто ми декодируем 6 каналів AC3 і кодуємо їх у 6 каналів AAC, зайвий раз використовуючи ресурси CPU. При перегляді відео це викликає періодичні підвисання.
Щоб включити так званий downmix, потрібно параметр 6 замінити на 2 і отримаємо
<UpperBound name="audio.channels" value="2" />
. Тоді файли з шестиканальної звуковою доріжкою будуть перетворюватися в стерео.
Для більшості користувачів цей варіант буде прийнятним. Але не для тих, у кого є файли з шестиканальної доріжкою AAC. В даному випадку шестиканальний AAC буде преобразоываваться в stereo AAC. А це знову трата ресурсів процесора і періодичні зависання при перегляді відео. Я вважав, що колдование з профілями може вирішити проблему, але, на жаль, в поточній версії Plex такі винятки можливі. На форумі Plex вже два тижні без відповіді висить запрос про додавання такої опції.
Єдиний варіант для вирішення цієї проблеми я побачив в підміні бінарника
Plex Transcoder
на скрипт, який буде формувати необхідні параметри при наявності AAC доріжки у відео файл.
magic.sh
#!/bin/bash
# This script disables transcode videos for which already have aac audio

magic=0
i=0
input=false
for arg in "$@"; do
((i++))
next=$((i+1))
if [[ "$arg" == " і" ]]; then
input=true
fi
if [[ "$arg" =~ -codec:[0-9] && "${@:$next:1}" == "aac" && $magic == 0 && $input == false ]]; then
((magic++))
continue
fi
if [[ "$arg" == "aac" && $magic == 1 ]]; then
((magic++))
continue
fi
if [[ "$arg" == "-codec:1" && $magic == 2 ]]; then
((magic++))
fi
if [[ "$arg" == "aac" && $magic == 3 ]]; then
args[$i]="copy"
((magic++))
continue 
fi
if [[ "$arg" == "-ar:1" && $magic == 4 ]]; then
args[$i]="-copypriorss:1"
((magic++))
continue 
fi
if [[ "$arg" == "48000" && $magic == 5 ]]; then
args[$i]="0"
((magic++))
continue 
fi
if [[ "$arg" == "-channel_layout:1" && $magic == 6 ]]; then
((magic++))
continue 
fi
if [[ "$arg" == "stereo" && $magic == 7 ]]; then
((magic++))
continue 
fi
if [[ "$arg" == "-b:1" && $magic == 8 ]]; then
((magic++))
continue 
fi
if [[ "$arg" == "256k" && $magic == 9 ]]; then
((magic++))
continue 
fi
args[$i]=$(printf "%q" "$arg")
done
set -- "${args[@]}"
eval "/opt/plex/Application/Resources/Plex\ Transcoder_ $@"

посилання на github: https://github.com/kayrus/plex/blob/master/magic.sh
При тестуванні з'ясувалося, що даний хак непогано працює з відео файлами з моєї медіатеки.
Docker
Тепер подивимося, як все це обернути в образ Docker. Як мінімум повинні виконуватися наступні умови:
  • Доступ з контейнера можливий тільки до певних директорій.
  • Plex не повинен запускатися з під root, навіть всередині контейнера.
  • Чому б не використовувати systemd для запуску контейнера з Plex?
Далі наведу витяги з
Dockefile
, який я використовую.
Копіюємо і розпаковуємо скачаний архів (можна отримати безпосередньо через wget, але використовуваної мною конфігурації це заборонено). Використовую
COPY
замість
ADD
щоб уникнути автоматичного розпакування архіву, в даному випадку в цьому немає необхідності. Вираз
|| true
дозволяє проігнорувати повідомлення gzip про сміття після кінця архіву.
COPY PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg /tmp/plex_media_server.tar
RUN { dd if=/tmp/plex_media_server.tar bs=22954 skip=1 status=none | tar -xzf - -C /opt/plex/Application || true; } && rm -f /tmp/plex_media_server.tar

Додаємо в контейнер непривілейованого системного користувача
plex
.
RUN useradd -r -d /var/lib/plex -s /sbin/nologin plex

Активізуємо downmix:
RUN sed -i 's/name="audio.channels" value="6"/name="audio.channels" value="2"/' /opt/plex/Application/Resources/Profiles/Web.xml

Всі подальші дії у контейнері будуть виконуватися від користувача
plex
.
USER plex

Помічаємо шляху
/var/lib/plex
(для збереження стану бази медіа файлів) і
/media
(шлях для медіа файлів) як зовнішні тома:
VOLUME ["/var/lib/plex","/media"]

Запуск контейнера
В команді нижче ми транслюємо стандартний порт 32400 у 80-ї http порт, монтуємо шлях
/home/plex
на
/var/lib/plex
всередині контейнера
/home/user/media
на
/media
.
$ docker run --name plex --hostname plex --rm -p 80:32400 -v /home/plex:/var/lib/plex -v /home/user/media:/media pleximage

systemd
Unit файл, який я використовую для запуску контейнера plex.
[Unit]
Description=Plex Media Server
After=docker.service
Requires=docker.service

[Service]
Environment=MEDIA_LIB=/home/user/media
Environment=CONFIG_DIR=/var/lib/plex
Environment=DOCKER_IMAGE=kayrus/plex
Environment=PLEX_INT_PORT=32400
Environment=PLEX_EXT_PORT=32400
# Remove old Plex container
ExecStarPre=-/usr/bin/docker rm plex
ExecStart=/usr/bin/docker run --name plex --hostname plex --rm -p ${PLEX_EXT_PORT}:${PLEX_INT_PORT} -v ${CONFIG_DIR}:/var/lib/plex -v ${MEDIA_LIB}:/media ${DOCKER_IMAGE}
# Fix foreign network which requires Plex login/signup
ExecStartPost=/sbin/iptables -t nat -I POSTROUTING -o docker0 -p tcp -m tcp --dport ${PLEX_INT_PORT} -j MASQUERADE
ExecStopPost=-/sbin/iptables -t nat -D POSTROUTING -o docker0 -p tcp -m tcp --dport ${PLEX_INT_PORT} -j MASQUERADE
ExecStop=/usr/bin/docker stop plex
# Remove pidfile stop after which prevents Plex start server
ExecStopPost=/bin/rm -f ${CONFIG_DIR}/Library/Application\x20Support/Plex\x20Media\x20Server/plexmediaserver.pid

[Install]
WantedBy=multi-user.target

https і nginx
Щоб мати доступ до Plex з інтернету, рекомендується використовувати HTTPS з'єднання. Якщо не хочеться реєструватися і платити гроші за додаткові можливості Plex, то сертифікат можна настроїти самостійно. Можна використовувати самопідписаний сертифікат, можна використовувати сертифікат від let's Encrypt. Але в кінцевому підсумку конфігураційний файл nginx буде виглядати приблизно так:
nginx_plex.conf
# Реалізуємо автоматичний редирект на Plex dashboard, спочатку у безкоштовній версії це не передбачено.
map $request_method$request_uri$http_referer $do_redirect {
"GET/" 1;
default 0;
}

server {
# Listen only HTTPS socket
listen [::]:443;

# Enter your domain here
server_name plex.example.com;

# Configure your SSL certificates here
ssl on;
include ssl.conf;

ssl_trusted_certificate ssl/ca-certs.pem;
ssl_certificate ssl/plex.example.com.pem;
ssl_certificate_key ssl/plex.example.com-key.pem;

# Protect Plex by basic auth
auth_basic "denied";
auth_basic_user_file .htpasswd;

# Redirect to the dashboard Plex
if ($do_redirect = 1) {
return 302 https://$host/web;
}

# Default location
location / {
# Не будемо передавати в Plex наші паролі
proxy_set_header Authorization "";
proxy_buffering off;
proxy_pass http://localhost:32400;
}

# Websockets location
location /:/websockets/ {
# Не будемо передавати в Plex наші паролі
proxy_set_header Authorization "";
proxy_buffering off;
proxy_pass http://localhost:32400;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

Посилання:
  • Репозиторій з інформацією про те, як запустити Plex в Docker контейнері під архітектурою ARM: https://github.com/kayrus/plex
  • Репозиторій з інформацією про те, як запустити Emby Media Server Docker контейнері під архітектурою ARM: https://github.com/kayrus/emby
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.