Skip to content

Instantly share code, notes, and snippets.

@prochor666
Created March 9, 2020 18:47
Show Gist options
  • Save prochor666/d47cd8e1eb07255c248bcbe6614f5c02 to your computer and use it in GitHub Desktop.
Save prochor666/d47cd8e1eb07255c248bcbe6614f5c02 to your computer and use it in GitHub Desktop.
Bash VOD/HLS media convertor
#!/bin/bash
# Sanatorium media convertor
# prochor, 2020
# prochor666@gmail.com
clear
# Create new website
echo -e "\e[38;5;14m|------------------------------------------- \e[0m"
echo -e "\e[38;5;14m| Sanatorium VOD/HLS media convertor \e[0m"
echo -e "\e[38;5;14m|------------------------------------------- \e[0m"
echo -e "\e[38;5;14m| \e[0m"
echo -e "\e[38;5;14m| * ffmpeg HLS convertor \e[0m"
echo -e "\e[38;5;14m| \e[0m"
echo -e "\e[38;5;14m| prochor666@gmail.com \e[0m"
echo -e "\e[38;5;14m|------------------------------------------- \e[0m"
echo -e " "
#set -e
# Usage scrintname.sh SOURCE_FILE
[[ ! "${1}" ]] && echo "Usage: ${0} SOURCE_FILE" && exit 1
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
MEDIAFILE=$1
SOURCEDIR="$ROOT/src"
TARGETDIR="$ROOT/vod/hs"
FILENAME=$(basename -- "$MEDIAFILE")
EXT="${FILENAME##*.}"
FNO="${FILENAME%.*}"
echo $SOURCEDIR/$MEDIAFILE
CHECK=$(md5sum "$SOURCEDIR/$MEDIAFILE" | awk '{ print $1 }')
NEWFILE="${CHECK[0]}"
WEBUSER=$(ls -ld "$ROOT/index.php" | awk '{print $3}')
WEBGROUP=$(ls -ld "$ROOT/index.php" | awk '{print $4}')
echo $NEWFILE
if [ ! -d "$TARGETDIR/$NEWFILE" ]; then
mkdir "$TARGETDIR/$NEWFILE"
chown -R $WEBUSER:$WEBGROUP "$TARGETDIR/$NEWFILE"
fi
# comment/add lines here to control which OUTPUTRESOLUTIONS would be created
OUTPUTRESOLUTIONS=(
# resolution bitrate audio-rate
"426x240 400k 64k"
"640x360 800k 96k"
"842x480 1400k 128k"
"1280x720 2800k 128k"
"1920x1080 5000k 192k"
)
SEGMENT_TARGET_DURATION=4 # try to create a new segment every X seconds
MAX_BITRATE_RATIO=1.07 # maximum accepted bitrate offset
RATE_MONITOR_BUFFER_RATIO=1.5 # maximum buffer size between bitrate conformance checks
#########################################################################
SOURCE="$SOURCEDIR/$MEDIAFILE"
TARGET="$TARGETDIR/$NEWFILE"
MAXHEIGHT="$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 ${SOURCE})"
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 OUTPUTRESOLUTIONS
STATIC_PARAMS="-c:a aac -ar 48000 -c:v h264 -profile:v main -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="-hide_banner -y"
CAN_START=0
MASTER_PLAYLIST="#EXTM3U
#EXT-X-VERSION:3
"
CMD=""
for OUTPUTRESOLUTION in "${OUTPUTRESOLUTIONS[@]}"; do
# drop extraneous spaces
OUTPUTRESOLUTION="${OUTPUTRESOLUTION/[[:space:]]+/ }"
# OUTPUTRESOLUTION fields
RESOLUTION="$(echo ${OUTPUTRESOLUTION} | cut -d ' ' -f 1)"
BITRATE="$(echo ${OUTPUTRESOLUTION} | cut -d ' ' -f 2)"
AUDIORATE="$(echo ${OUTPUTRESOLUTION} | cut -d ' ' -f 3)"
# calculated fields
WIDTH="$(echo ${RESOLUTION} | grep -oE '^[[:digit:]]+')"
HEIGHT="$(echo ${RESOLUTION} | grep -oE '[[:digit:]]+$')"
if [ "$MAXHEIGHT" -ge "$HEIGHT" ]; then
CAN_START=1
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}/seg${NAME}_%03d.ts ${TARGET}/${NAME}.m3u8"
# add OUTPUTRESOLUTION entry in the master playlist
MASTER_PLAYLIST+="#EXT-X-STREAM-INF:BANDWIDTH=${BANDWIDTH},RESOLUTION=${RESOLUTION}\n${NAME}.m3u8\n"
fi
done
# can not start
if [ "$CAN_START" -eq 0 ]; then
echo "Error setting up file $SOURCEDIR/$MEDIAFILE max height is $MAXHEIGHT"
fi
# start conversion
if [ "$CAN_START" -eq 1 ]; then
echo -e "Executing command:\nffmpeg ${MISC_PARAMS} -i ${SOURCE} ${CMD}"
ffmpeg ${MISC_PARAMS} -i ${SOURCE} ${CMD}
# create master playlist file
echo -e "${MASTER_PLAYLIST}" > ${TARGET}/playlist.m3u8
chown -R $WEBUSER:$WEBGROUP "$TARGETDIR/$NEWFILE"/*
echo "Done setting up file $SOURCEDIR/$MEDIAFILE"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment