Skip to content

Instantly share code, notes, and snippets.

@fitz123
Last active May 24, 2019 19:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fitz123/e0e2921ae81b4b5136ec7b3b53079807 to your computer and use it in GitHub Desktop.
Save fitz123/e0e2921ae81b4b5136ec7b3b53079807 to your computer and use it in GitHub Desktop.
Supervisor ffmpeg transcoding job and HLS playlist generator
#!/bin/bash
###############################################################################
#
# Description:
# Script generates Nimble session-friendly playlists
# (parent playlist with sub-playlists inside) and supervisor program/job
# of ffmpeg process which pulls from provided source address $ch_source and
# pushes to localhost nibmle-origin server as a name $ch_res for later HLS transmuxing
#
# To generate playlists and jobs - templates are used (playlist.template,*-job.template)
#
# As a result followed files will be generated:
# * $supervisor_confd/ffmpeg-$ch_name - supervisor job
# * $playlist_root/$ch_name-$ch_res.m3u8 - "parent" playlist for stream
# (if 3rd arg is 'abr' also playlist for each resolution will be created)
# * $playlist_root/$ch_name.m3u8 - symlink to parent playlist
#
# No output and 0 exit status means everything good, job is started!
#
# Usage example:
# sudo ./job_gen.sh rtmp://27.131.165.98:1935/dvrorigin1/hd-foxpremiumeng_1080 fox abr
#
# Parameters:
# $1) source to grab stream from
# $2) name to publish stream as. Also creates supervisor "program" with the same name
# $3) template to apply for job and playlist generation.
# Possibly values are:
# * 240p, 360p, 480p, 720p - each value generates single transcoding job and playlist
# * copy - re-publishes stream without transcoding and creates playlist
# * abr - generates transcoding job for multiple resolutions,
# creates combined playlist with all resolutions for ABR streaming and
# playlist for each resolution separatelly
#
# script: job_gen.sh
# ver: 0.1
# author: anton.lugovoi@yandex.ru
#
###############################################################################
playlist_root='/srv/media-server/live'
supervisor_confd='/etc/supervisor/conf.d'
resolutions='720p 480p 360p 240p copy'
templates='240p-job.template 480p-job.template abr-job.template copy-job.template 360p-job.template 720p-job.template abr-playlist.template playlist.template'
bw_240p='500000'
bw_360p='1000000'
bw_480p='1500000'
bw_720p='3000000'
bw_copy='9999999'
ch_source="$1"
ch_name="$2"
ch_res="$3"
# fix resolution parameter if passed without trailing p
[ ! 'copy' == "$ch_res" -a ! 'abr' == "$ch_res" -a ! 'p' == "${ch_res:(-1)}" ] && ch_res=$3p
##
# checks
##
print_readme() {
printf '
Usage example:
sudo ./job_gen.sh rtmp://27.131.165.98:1935/dvrorigin1/hd-foxpremiumeng_1080 fox abr
Parameters:
$1) source to grab stream from
$2) name to publish stream as. Also creates supervisor "program" with a such name
$3) template to apply for job and playlist generation.
Possibly values are:
* 240p, 360p, 480p, 720p - each value generates single transcoding job and playlist
* copy - generates "copy" and playlist
* abr - generates transcoding job for multiple resolutions,
creates combined playlist with all resolutions for ABR streaming and
playlist for each resolution separatelly
'
}
# check if run not as root
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit 1
fi
# check arguments number
if [ "$#" -ne 3 ]; then
echo "Error: Illegal number of parameters"
print_readme
exit 1
fi
# check resolution
if [[ ! "$resolutions" =~ "$ch_res" ]]; then
if [ ! 'abr' == "$ch_res" ]; then
echo "Error: Illegal resolution $ch_res"
print_readme
exit 1
fi
fi
# check supervisor config directory
[ -d "$supervisor_confd" ] || { echo Error: directory "$supervisor_confd" directory does not exist ; exit 1 ;}
# check playlist root
[ -d "$playlist_root" ] || { echo Error: directory "$playlist_root" directory does not exist ; exit 1 ;}
# check stream directories
for dir in $resolutions; do [ -d "$playlist_root/$dir" ] || { echo Error: directory "$playlist_root/$dir" directory does not exist ; exit 1 ;}; done
# check templates required for jobs and playlists generation
for tpl in $templates; do
if [ ! -e "$tpl" -o ! -f "$tpl" -o ! -s "$tpl" -o ! -r "$tpl" ]; then
echo Error: template "$tpl" does not exist, not a file, zero size or not readable
exit 1
fi
done
# check ffmpeg/ffprobe installed
command -v ffmpeg >/dev/null 2>&1 || { echo >&2 "I require ffmpeg but it's not installed. Aborting."; exit 1; }
command -v ffprobe >/dev/null 2>&1 || { echo >&2 "I require ffprobe but it's not installed. Aborting."; exit 1; }
# check source
ffprobe "$ch_source" 2>&1 | egrep "^\s+Stream.+Video." >/dev/null 2>&1 || { echo Error: stream source $ch_source has no video or invalid ; exit 1 ;}
##
# generate playlists
##
if [ $ch_res == 'abr' ]; then
# playlist with all resolutions
sed -e "s;%channel_name%;$ch_name;g" $ch_res-playlist.template > $playlist_root/$ch_name-$ch_res.m3u8
ln -f -s $playlist_root/$ch_name-$ch_res.m3u8 $playlist_root/$ch_name.m3u8
for res in $resolutions; do
# playlist for nimble sessions compatibility for each resolution inside abr group
bw=bw_$res
sed -e "s;%channel_name%;$ch_name;g" -e "s;%resolution%;$res;g" -e "s;%bandwidth%;${!bw};g" playlist.template > $playlist_root/$ch_name-$res.m3u8
done
else
# playlist for nimble sessions compatibility
bw=bw_$ch_res
sed -e "s;%channel_name%;$ch_name;g" -e "s;%resolution%;$ch_res;g" -e "s;%bandwidth%;${!bw};g" playlist.template > $playlist_root/$ch_name-$ch_res.m3u8
ln -f -s $playlist_root/$ch_name-$ch_res.m3u8 $playlist_root/$ch_name.m3u8
fi
##
# generate supervisor job
##
# detect if stream is http then apply additional parameters for input robustness
input_proto=$(echo "$ch_source" | cut -d: -f1)
if [ "$input_proto" == 'http' ]; then
stream_input_parameters='-reconnect_at_eof 1 -reconnect_streamed 1'
elif [ "$input_proto" == "$ch_source" ]; then
stream_input_parameters='-re'
else
stream_input_parameters=''
fi
sed -e "s;%channel_source%;$ch_source;g" -e "s;%channel_name%;$ch_name;g" -e "s;%stream_input_parameters%;$stream_input_parameters;g" $ch_res-job.template > $supervisor_confd/ffmpeg-$ch_name.conf
supervisorctl update && sleep 3 && \
job_status=$(supervisorctl status $ch_name | awk '{print $2}') && \
[ ! $job_status == 'RUNNING' ] && echo Job has been started but something went wrong. Job status is $job_status && exit 1
exit 0
@fitz123
Copy link
Author

fitz123 commented Jul 7, 2016

abr-playlist.template:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE,BANDWIDTH=3000000,RESOLUTION=1280x720
720p/%channel_name%/index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE,BANDWIDTH=1500000,RESOLUTION=852x480
480p/%channel_name%/index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE,BANDWIDTH=1000000,RESOLUTION=640x360
360p/%channel_name%/index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE,BANDWIDTH=500000,RESOLUTION=426x240
240p/%channel_name%/index.m3u8

playlist.template:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,CLOSED-CAPTIONS=NONE,BANDWIDTH=%bandwidth%
%resolution%/%channel_name%/index.m3u8

abr-job.template:

[program:%channel_name%]
autorestart=true
autostart=true
redirect_stderr=true
command=ffmpeg  -stream_loop -1 %stream_input_parameters%
                -i "%channel_source%"

                -c:a libfdk_aac -b:a 196k
                -c:v libx264 -preset veryfast -g 50 -r 25
                -s 1280x720 -b:v 2808k -minrate 2808k -maxrate 2808k -bufsize 5616k
                -flags +global_header 
                -f flv "rtmp://localhost:1935/720p/%channel_name%"

                -c:a libfdk_aac -b:a 128k
                -c:v libx264 -preset veryfast -g 50 -r 25
                -s 852x480 -b:v 1372k -minrate 1372k -maxrate 1372k -bufsize 2744k
                -flags +global_header 
                -f flv "rtmp://localhost:1935/480p/%channel_name%"

                -c:a libfdk_aac -b:a 96k
                -c:v libx264 -preset veryfast -g 50 -r 25
                -s 640x360 -b:v 904k -minrate 904k -maxrate 904k -bufsize 1808k
                -flags +global_header 
                -f flv "rtmp://localhost:1935/360p/%channel_name%"

                -c:a libfdk_aac -b:a 96k
                -c:v libx264 -preset veryfast -g 50 -r 25
                -s 426x240 -b:v 436k -minrate 436k -maxrate 436k -bufsize 872k
                -flags +global_header 
                -f flv "rtmp://localhost:1935/240p/%channel_name%"

copy-job.template:

[program:%channel_name%]
autorestart=true
autostart=true
redirect_stderr=true
command=ffmpeg  -stream_loop -1 %stream_input_parameters%
                -i "%channel_source%"

                -c:a copy
                -c:v copy
                -flags +global_header 
                -f flv "rtmp://localhost:1935/copy/%channel_name%"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment