|
#!/usr/bin/env bash |
|
|
|
set -e |
|
|
|
# Usage create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME] |
|
[[ ! "${1}" ]] && echo "Usage: create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME] AudioTrack GPUNumber" && exit 1 |
|
|
|
# comment/add lines here to control which renditions would be created |
|
renditions=( |
|
# resolution bitrate audio-rate |
|
"360x640 800k 96k" |
|
"480x842 1400k 128k" |
|
"720x1280 2800k 128k" |
|
"1080x1920 5000k 192k" |
|
) |
|
|
|
MAX_NVENC_SESSION=3 |
|
segment_target_duration=4 # try to create a new segment every X seconds |
|
max_bitrate_ratio=1.07 # maximum accepted bitrate fluctuations |
|
rate_monitor_buffer_ratio=1.5 # maximum buffer size between bitrate conformance checks |
|
|
|
######################################################################### |
|
|
|
source="${1}" |
|
target="${2}" |
|
if [[ ! "${target}" ]]; then |
|
target="${source##*/}" # leave only last component of path |
|
target="${target%.*}" # strip extension |
|
fi |
|
mkdir -p ${target} |
|
|
|
|
|
key_frames_interval="$(echo `ffprobe ${source} 2>&1 | grep -oE '[[:digit:]]+(.[[:digit:]]+)? fps' | grep -oE '[[:digit:]]+(.[[:digit:]]+)?'`*2 | bc || echo '')" |
|
key_frames_interval=${key_frames_interval:-50} |
|
key_frames_interval=$(echo `printf "%.1f\n" $(bc -l <<<"$key_frames_interval/10")`*10 | bc) # round |
|
key_frames_interval=${key_frames_interval%.*} # truncate to integer |
|
|
|
# static parameters that are similar for all renditions |
|
static_params="-map 0:v:${3} -map 0:a:${4} -c:a aac -ar 48000 -c:v h264_nvenc -profile:v main -preset:v fast -rc constqp -qp 30 -crf 20 -sc_threshold 0 " |
|
static_params+=" -g ${key_frames_interval} -keyint_min ${key_frames_interval} -hls_time ${segment_target_duration}" |
|
static_params+=" -hls_playlist_type vod" |
|
|
|
# misc params |
|
misc_params="-vsync 0 -hide_banner -y " |
|
|
|
master_playlist="#EXTM3U |
|
#EXT-X-VERSION:3 |
|
" |
|
cmd="" |
|
session=0 |
|
for rendition in "${renditions[@]}"; do |
|
# drop extraneous spaces |
|
rendition="${rendition/[[:space:]]+/ }" |
|
|
|
# rendition fields |
|
resolution="$(echo ${rendition} | cut -d ' ' -f 1)" |
|
bitrate="$(echo ${rendition} | cut -d ' ' -f 2)" |
|
audiorate="$(echo ${rendition} | cut -d ' ' -f 3)" |
|
|
|
# calculated fields |
|
width="$(echo ${resolution} | grep -oE '^[[:digit:]]+')" |
|
height="$(echo ${resolution} | grep -oE '[[:digit:]]+$')" |
|
maxrate="$(echo "`echo ${bitrate} | grep -oE '[[:digit:]]+'`*${max_bitrate_ratio}" | bc)" |
|
bufsize="$(echo "`echo ${bitrate} | grep -oE '[[:digit:]]+'`*${rate_monitor_buffer_ratio}" | bc)" |
|
bandwidth="$(echo ${bitrate} | grep -oE '[[:digit:]]+')000" |
|
name="${height}p" |
|
|
|
cmd+=" ${static_params} -vf scale=w=${width}:h=${height}:force_original_aspect_ratio=decrease" |
|
cmd+=" -b:v ${bitrate} -maxrate ${maxrate%.*}k -bufsize ${bufsize%.*}k -b:a ${audiorate}" |
|
cmd+=" -hls_segment_filename ${target}/${name}_%03d.ts ${target}/${name}.m3u8" |
|
|
|
# add rendition entry in the master playlist |
|
master_playlist+="#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth},RESOLUTION=${resolution}\n${name}.m3u8\n" |
|
|
|
session=$((session + 1)) |
|
echo "SESSION:"$session |
|
if [ $session -eq $MAX_NVENC_SESSION ] |
|
then |
|
session=0 |
|
# start conversion |
|
echo -e "Executing command:\nffmpeg ${misc_params} -i ${source} ${cmd}" |
|
echo "====================================" |
|
ffmpeg ${misc_params} -i ${source} ${cmd} |
|
cmd="" |
|
fi |
|
done |
|
|
|
if [ -n "$cmd" ] |
|
then |
|
# start conversion |
|
echo -e "Executing command:\nffmpeg ${misc_params} -i ${source} ${cmd}" |
|
echo "====================================" |
|
ffmpeg ${misc_params} -i ${source} ${cmd} |
|
fi |
|
|
|
# create master playlist file |
|
echo -e "${master_playlist}" > ${target}/playlist.m3u8 |
|
|
|
echo "Done - encoded HLS is at ${target}/" |