|
#!/usr/bin/env bash |
|
# A modified version of the script for creating HLS streams of videos. From mbar42: |
|
# https://gist.github.com/mrbar42/ae111731906f958b396f30906004b3fa |
|
# |
|
# This version adds support for burning in subtitles and selecting different languages. |
|
|
|
set -e |
|
|
|
# Usage create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME] |
|
[[ ! "${1}" ]] && echo "Usage: create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME]" && exit 1 |
|
|
|
# comment/add lines here to control which renditions would be created |
|
renditions=( |
|
# resolution bitrate audio-rate audio_stream subtitle_stream |
|
#"426x240 400k 64k eng" |
|
# "640x360 800k 96k" |
|
"842x480 2000k 128k jpn eng" |
|
#"1280x720 2800k 128k" |
|
#"1920x1080 5000k 192k eng eng" |
|
#"1280x720 2000k 192k jpn eng" |
|
#"1280x720 3000k 192k jpn eng" |
|
"1280x720 4000k 192k jpn eng" |
|
"1920x1080 5000k 192k jpn eng" |
|
"1920x1080 10000k 192k jpn eng" |
|
"1920x1080 15000k 192k jpn eng" |
|
) |
|
|
|
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="-c:a aac -ac 2 -ar 48000 -c:v h264 -profile:v main -crf 15 -sc_threshold 0 -pix_fmt yuv420p -map 0: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" |
|
|
|
master_playlist="#EXTM3U |
|
#EXT-X-VERSION:3 |
|
" |
|
cmd="" |
|
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)" |
|
audiostream="$(echo ${rendition} | cut -d ' ' -f 4)" |
|
subtitlestream="$(echo ${rendition} | cut -d ' ' -f 5)" |
|
|
|
|
|
# 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_${bitrate}bitrate" |
|
|
|
audiostreamindex="" |
|
if [[ -n "$audiostream" && "$audiostream" != "none" ]]; then |
|
name+="_${audiostream}audio" |
|
audiostreamindex="$(ffprobe -loglevel error -select_streams a -show_entries stream=index:stream_tags=language -of csv=p=0 "$source" | grep "$audiostream" | cut -d ',' -f 1)" |
|
else |
|
audiostreamindex="$(ffprobe -loglevel error -select_streams a -show_entries stream=index:stream_tags=language -of csv=p=0 "$source" | head -n1 | cut -d ',' -f 1)" |
|
fi |
|
|
|
subtitlestreamindex="" |
|
if [[ -n "$subtitlestream" && "$subtitlestream" != "none" ]]; then |
|
name+="_${subtitlestream}subs" |
|
subtitlestreamindex="$(ffprobe -loglevel error -select_streams s -show_entries stream=index:stream_tags=language -of csv=p=0 "$source" | grep "$subtitlestream" | awk NF | nl --number-separator=',' --number-width=1 --starting-line-number=0 | cut -d ',' -f 1)" |
|
fi |
|
|
|
cmd+=" ${static_params} -vf scale=w=${width}:h=${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:-1:-1:color=black" |
|
if [[ -n "$subtitlestreamindex" ]]; then # only burn in subtitles if sub language was specified, otherwise skip it and FFMPEG will use the defaults (of not including subtitles) |
|
cmd+=",subtitles=$source:stream_index=$subtitlestreamindex" |
|
fi |
|
cmd+=" -b:v ${bitrate} -maxrate ${maxrate%.*}k -bufsize ${bufsize%.*}k -b:a ${audiorate}" |
|
if [[ -n "$audiostreamindex" ]]; then # only choose an audio stream if one was specified, otherwise skip it and FFMPEG will use the defaults |
|
cmd+=" -map 0:$audiostreamindex" |
|
fi |
|
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" |
|
done |
|
|
|
# start conversion |
|
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 |
|
|
|
echo "Done - encoded HLS is at ${target}/" |