Как оптимизировать кодирование и упаковку видео с помощью ffmpeg и shaka-packager

Я пытаюсь кодировать и упаковать загруженные видео для веб-сайта LMS, где размер видео может отличаться. Как я могу написать сценарий sh, который преобразует и упаковывает данное видео в зависимости от его размера (например, если данное разрешение видео больше 720p и меньше 1080p FFmpeg должен конвертировать видео в 2 размера ([360p, 720p], затем shaka-packager должен их упаковать).

Пока у меня есть этот скрипт, предполагающий, что разрешение входного видео составляет 1080p (или 1080p ‹= size‹ 4k)

#!/bin/sh
pwd
URL="$1"
ID="$2"
FOLDER="$3"

if [ -z "$URL" ];then
    echo "Must input a file"
    $SHELL
    exit
fi

DIR="$FOLDER/$ID"
OUTDIR="$DIR/cmaf"
mkdir -p -v $DIR
mkdir -p -v $OUTDIR

GOP_SIZE=50
FPS=25
CRF=28

INPUT="$DIR/input"
wget -c -O $INPUT $URL &&

if [ ! -f $FILE ]; then
    echo "$FILE does not exists"
    $SHELL
    exit
fi

ffmpeg -i $INPUT -y \
-threads 1 \
-c:v libx264 -crf $CRF -profile:v high -pix_fmt yuv420p \
-keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 \
-color_primaries 1 -color_trc 1 -colorspace 1 -movflags +faststart \
-c:a aac -b:a 128k -ar 44100 \
-r $FPS \
"$DIR/input.mp4" &&

ffmpeg -i "$DIR/input.mp4" -y \
-threads 1 \
-vn -acodec copy "$DIR/a.mp4" \
-vf scale=640:360 -an "$DIR/360p.mp4" \
-vf scale=1280:720 -an "$DIR/720p.mp4" \
-vf scale=1920:1080 -an "$DIR/1080p.mp4" &&

rm -R $OUTDIR

packager \
in="$DIR/a.mp4",stream=audio,output="$OUTDIR/a.mp4",drm_label=AUDIO \
in="$DIR/360p.mp4",stream=video,output="$OUTDIR/360p.mp4",drm_label=SD \
in="$DIR/720p.mp4",stream=video,output="$OUTDIR/720p.mp4",drm_label=HD \
in="$DIR/1080p.mp4",stream=video,output="$OUTDIR/1080p.mp4",drm_label=HD \
--enable_raw_key_encryption \
--keys label=AUDIO:key_id=f3c5e0761e6654b28f8049c778b23947:key=a4637a153a443df9eed0593043db7517,label=SD:key_id=abba277e8bcf552bbd2e86a434a9a5d7:key=69eaa807a6763af979e8d1940fb88397,label=HD:key_id=6d76f25cb17f5e76b8eaef6b7f582d87:key=cb541784c99737aef4fff74500c12ea7 \
--pssh 000000377073776800000000EDEF8BA979D64ACEA3C877DCD51D21ED00000071220F7465737420636F6E74656E74206967 \
--mpd_output "$OUTDIR/h264.mpd" \
--hls_master_playlist_output "$OUTDIR/h264_master.m3u8"

Приведенный выше сценарий сначала загружает видео по заданному URL-адресу, а затем преобразует его в соответствующий видеоформат перед изменением размера и упаковкой. Я предположил, что если я конвертирую видео перед масштабированием, это будет более производительно, чем преобразование и изменение размера каждый раз. Кроме того, я предполагал, что если я изменю размер до всех разрешений с помощью одной команды, это будет намного быстрее, но я думаю, что FFmpeg работает не так. Я живу в мире FFmpeg, не зная, как написать сценарий sh (или bash) лучше, чище и динамичнее для кодирования и упаковки видео для потоковой передачи в Интернете. Я думаю, что есть другие с той же проблемой или с таким же случаем. Так что любая помощь, исправление и рекомендация приветствуются


person Saidamir Botirov    schedule 16.11.2020    source источник
comment
Что вы здесь делаете, так это запускаете ffmpeg с вашими настройками, затем при втором вызове (i) повторно конвертируете все, включая (большую) производительность и некоторую потерю качества, и (ii) перезаписывая все ваши параметры из первого запуска с параметрами по умолчанию. Во-первых: просто присоединитесь к вашему второму вызову ffmpeg с первым. На самом деле нет необходимости запускать ffmpeg дважды. Во-вторых: если вы думаете о производительности, а не об абсолютном качестве, вы можете взглянуть на аппаратное ускорение процессоров Intel (QSV / VAAPI). Там вы получаете кодирование + масштабирование бесплатно с некоторой потерей качества по libx264.   -  person Suuuehgi    schedule 16.11.2020
comment
P.s. Вы используете множество очень специальных аргументов. Я рекомендую придерживаться значений по умолчанию для ffmpeg, если вам действительно не нужно иное.   -  person Suuuehgi    schedule 16.11.2020
comment
Спасибо, посмотрю на аппаратное ускорение по производительности. Но я думал, что каждый раз, когда FFmpeg запускает простую команду, например ffmpeg -i input.mov -s 640x320 -c:v libx264 -c:a copy output.mp4, он декодирует, изменяет размер, а затем кодирует видео. При 4-кратном изменении размера он выполняет 3 дополнительных декодирования, которые, как я думал, приводят к потере производительности, как я понимаю FFmpeg. Кроме того, я попробовал карту FFmpeg, сложную команду фильтрации, но безуспешно. Тогда я думаю, что есть люди, которые знают, как делать оптимальным образом, или это единственный способ, которым работает FFmpeg?   -  person Saidamir Botirov    schedule 16.11.2020
comment
Что касается особых аргументов, я собирался придерживаться значения ffmpeg по умолчанию, но в некоторых статьях в Интернете авторы рекомендуют использовать их для потокового видео.   -  person Saidamir Botirov    schedule 16.11.2020
comment
В Интернете есть множество различных (специализированных) вариантов использования и плохих примеров. Я просто хочу сказать: будьте осторожны с аргументами, которые вам не до конца понятны. Я добавил более длинный ответ ниже.   -  person Suuuehgi    schedule 16.11.2020


Ответы (1)


Для ясности я удалил некоторые аргументы из ваших команд (yuv420p и -profile:v high - значения по умолчанию, не изменяющие частоту кадров)

ffmpeg -i <input> -y \
-c:v libx264 -crf 28 -g 50    \
-c:a aac -b:a 128k -ar 44100  \
-movflags +faststart          \
<output> &&

ffmpeg -i <output> -y \
-vn -c:a copy "$DIR/a.mp4" \
-vf scale=640:360   -an "$DIR/360p.mp4" \
-vf scale=1280:720  -an "$DIR/720p.mp4" \
-vf scale=1920:1080 -an "$DIR/1080p.mp4"

При первом запуске ваш ввод будет декодирован и перекодирован с использованием libx264 с целевым значением качества 28 и ключевым кадром каждые 50 кадров.

Второй экземпляр декодирует его снова, угадывая кодировщик по расширению .mp4 (по умолчанию libx264), и перекодирует все три раза, используя значения по умолчанию -g 250 -crf 23 (я не уверен насчет -movflags +faststart).

Таким образом, вы (1) перезаписываете свои настройки с первого запуска, (2) выполняете дополнительный процесс декодирования и (3) имеете определенную потерю качества из-за нескольких кодировок с потерями.

Вам нужно объединить их в один вызов:

ffmpeg -i <input> -y \
-vn -c:a aac -b:a 128k -ar 44100 "$DIR/a.mp4" \
-c:v libx264 -crf 28 -g 50 -s 640x360  -movflags +faststart -an "$DIR/360p.mp4" \
-c:v libx264 -crf 28 -g 50 -s 1280x720 -movflags +faststart -an "$DIR/720p.mp4" \
-c:v libx264 -crf 28 -g 50 -s 19201080 -movflags +faststart -an "$DIR/1080p.mp4"

Кроме того, я бы держался подальше от особых аргументов, если вы действительно не знаете, что и почему вы их выбираете.


П.с.

Это команда, которая работает на моем ноутбуке с загрузкой ЦП 15%.

ffmpeg \
  -hwaccel qsv -c:v h264_qsv -i 'rtsp://109.98.78.106' \
  -an -c:v h264_qsv -global_quality 30 -vf "scale_qsv=h=360:w=-1"  "/tmp/360p.mp4" \
  -an -c:v h264_qsv -global_quality 30 -vf "scale_qsv=h=720:w=-1"  "/tmp/720p.mp4" \
  -an -c:v h264_qsv -global_quality 30 -vf "scale_qsv=h=1080:w=-1" "/tmp/1080p.mp4"

У него могут быть проблемы с цветом и / или качеством, но это компромисс с производительностью.

person Suuuehgi    schedule 16.11.2020
comment
Еще один вопрос, имеет ли значение установка порядка в команде FFmpeg? - person Saidamir Botirov; 17.11.2020
comment
Есть два типа опций: глобальные опции (например, -loglevel error) и опции ввода / вывода. Сначала идут глобальные параметры, а затем все остальные параметры ввода / вывода. Все (эти) параметры применяются ТОЛЬКО к следующему входному или выходному файлу и сбрасываются между файлами. Порядок среди них afaik значения не имеет. файл может быть в любом формате здесь, например поток. Разделителем является -i: все (кроме глобальных параметров) перед входными параметрами, все, что находится позади, является либо вариантом вывода, XOR, либо вариантом ввода, относящимся к следующему -i. - person Suuuehgi; 17.11.2020