Saidamir Botirov
Saidamir Botirov

Reputation: 53

How to optimize encoding and packaging videos using ffmpeg and shaka-packager

I'm trying to encode and package uploaded videos for an LMS website where video size may differ. How can I write a sh script that converts and packages the given video based on its size (For ex. if the given video resolution is bigger than 720p and less than 1080p FFmpeg should convert videos in 2 sizes [360p, 720p] then shaka-packager should package them).

So far I have this script assuming that input video resolution is 1080p (or 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"

The above script first downloads a video by a given URL then converts it to appropriate video format before resizing and packaging. I assumed if I convert the video before scaling would be more performant than every time converting and resizing it. Also, I assumed if I resize to all resolutions in one command it would be much faster, but I think that is not how FFmpeg works. I'm stack in the world of FFmpeg not knowing how to write sh(or bash) script better, cleaner and dynamic for encoding and packaging videos for online streaming. I think there are others with the same problem or the same case. So any help, fix and recommendation is appreciated

Upvotes: 0

Views: 1763

Answers (1)

Suuuehgi
Suuuehgi

Reputation: 5010

For the sake of clarity, I stripped some arguments from your commands (yuv420p and -profile:v high are defaults, not changing frame-rate)

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"

The first run will decode your input and re-encode it using libx264 with quality-target 28 and a keyframe every 50 frames.

The second instance will decode it again, guessing an encoder by the .mp4 extension -- defaulting to libx264 --, and re-encodes everything three times by using the default values -g 250 -crf 23 (I'm not sure about -movflags +faststart).

So you are (1) overwriting your settings from the first-run, (2) having an additional decode process and (3) having a certain quality loss due to multiple lossy encodings.

What you want is to combine these into one invocation:

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"

Additionally, I would stay away from special arguments unless you really know what and why you are choosing them.


P.s.

This is a command that runs with 15 % CPU utilization on my laptop.

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"

It might have some color and / or quality issues but this is a performance trade-off.

Upvotes: 1

Related Questions