Add FFMPEG_INPUT_FLAGS env variable for ffmpeg
Add FFMPEG_INPUT_FLAGS env variable for ffmpeg

--- a/HLS-Stream-Creator.sh
+++ b/HLS-Stream-Creator.sh
@@ -50,11 +50,12 @@
 # Video codec for the output video. Will be used as an value for the -vcodec argument
 VIDEO_CODEC=${VIDEO_CODEC:-"libx264"}
 
-# Video codec for the output video. Will be used as an value for the -acodec argument
-AUDIO_CODEC=${AUDIO_CODEC:-"libfdk_aac"}
+# Audio codec for the output video. Will be used as an value for the -acodec argument
+AUDIO_CODEC=${AUDIO_CODEC:-"aac"}
 
 # Additional flags for ffmpeg
 FFMPEG_FLAGS=${FFMPEG_FLAGS:-""}
+FFMPEG_INPUT_FLAGS=${FFMPEG_INPUT_FLAGS:-""}
 
 # If the input is a live stream (i.e. linear video) this should be 1
 LIVE_STREAM=${LIVE_STREAM:-0}
@@ -65,6 +66,12 @@
 
 # Determines whether the processing for adaptive streams should run sequentially or not
 NO_FORK=${NO_FORK:-0}
+
+# Path Prefix (Not incl. Key)
+PATH_PREFIX=${PATH_PREFIX:-''}
+
+# Key Path Prefix
+KEY_PREFIX=${KEY_PREFIX:-''}
 
 # Lets put our functions here
 
@@ -95,6 +102,13 @@
 	-p	Playlist filename
 	-t	Segment filename prefix
 	-S	Segment directory name (default none)
+	-e	Encrypt the HLS segments (default none)
+	-2	2-pass encoding
+	-q	Quality (changes to CRF)
+	-C	Constant Bit Rate (CBR as opposed to AVB)
+	-u	Path Prefix
+	-k	Key Prefix
+	-K	Key Name
 
 Deprecated Legacy usage:
 	HLS-Stream-Creator.sh inputfile segmentlength(seconds) [outputdir='./output']
@@ -116,8 +130,37 @@
 bitrate="$3"
 infile="$4"
 
-$FFMPEG -i "$infile" \
-    -loglevel error -y \
+
+# Resolution comes from global $resolution
+#
+# See HLS-27
+#
+# I don't like this use of $resolution when it's not local, but there was already a use 
+# of non-local $br in here so I'll fix this later
+
+
+local PASSVAR=
+if $TWOPASS; then
+	local LOGFILE="$OUTPUT_DIRECTORY/bitrate$br"
+	PASSVAR="-passlogfile \"$LOGFILE\" -pass 2"
+
+	$FFMPEG -i "$infile" \
+		-pass 1 \
+		-passlogfile "$LOGFILE" \
+		-an \
+		-vcodec libx264 \
+		-f mpegts \
+		$bitrate \
+		$resolution \
+		$FFMPEG_ADDITIONAL \
+		-loglevel error -y \
+		/dev/null
+fi
+
+$FFMPEG $FFMPEG_INPUT_FLAGS \
+    -i "$infile" \
+    $PASSVAR \
+    -y \
     -vcodec "$VIDEO_CODEC" \
     -acodec "$AUDIO_CODEC" \
     -threads "$NUMTHREADS" \
@@ -128,6 +171,7 @@
     -segment_list "$playlist_name" \
     -segment_time "$SEGLENGTH" \
     -segment_format mpeg_ts \
+    $resolution \
     $bitrate \
     $FFMPEG_ADDITIONAL \
     $FFMPEG_FLAGS \
@@ -144,11 +188,20 @@
 function appendVariantPlaylistentry(){
 playlist_name=$1
 playlist_path=$2
-playlist_bw=$(( $3 * 1000 )) # bits not bytes :)
+bw_statement=$3
+m3u8_resolution=''
+
+if [[ "$bw_statement" == *"-"* ]]
+then
+    m3u8_resolution=",RESOLUTION=$(echo "$bw_statement" | cut -d- -f2)"
+    bw_statement=$(echo "$bw_statement" | cut -d- -f1) 
+fi
+
+playlist_bw=$(( $bw_statement * 1000 )) # bits not bytes :)
 
 cat << EOM >> "$playlist_name"
-#EXT-X-STREAM-INF:BANDWIDTH=$playlist_bw
-$playlist_path
+#EXT-X-STREAM-INF:BANDWIDTH=$playlist_bw$m3u8_resolution
+$PATH_PREFIX$playlist_path
 EOM
 
 }
@@ -168,7 +221,7 @@
 	  then
 		echo "Encoding for bitrate ${BITRATE_PROCESSES[$i]}k completed"
 
-		if [ "$LIVE_STREAM" == "1" ]
+		if [ "$LIVE_STREAM" == "1" ] && [ `grep 'EXT-X-ENDLIST' "$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_${BITRATE_PROCESSES[$i]}.m3u8" | wc -l ` == "0" ]
 		then
 		    # Correctly terminate the manifest. See HLS-15 for info on why
 		    echo "#EXT-X-ENDLIST" >> "$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_${BITRATE_PROCESSES[$i]}.m3u8"
@@ -179,25 +232,72 @@
 	  fi
     done
     PIDS=("${PIDS[@]}") # remove any nulls
+    BITRATE_PROCESSES=("${BITRATE_PROCESSES[@]}") # remove any nulls
     sleep 1
 done
 }
 
+function encrypt(){
+# Encrypt the generated segments with AES-128 bits
+
+
+    # Only run the encryption routine if it's been enabled  (and not blocked)
+    if [ ! "$ENCRYPT" == "1" ] || [ "$LIVE_STREAM" == "1" ]
+    then
+        return
+    fi
+
+    echo "Generating Encryption Key"
+    KEY_FILE="$OUTPUT_DIRECTORY/${KEY_NAME}.key"
+
+    openssl rand 16 > $KEY_FILE
+    ENCRYPTION_KEY=$(cat $KEY_FILE | hexdump -e '16/1 "%02x"')
+
+    echo "Encrypting Segments"
+    for SEGMENT_FILE in ${OUTPUT_DIRECTORY}/*.ts
+    do
+        SEG_NO=$( echo "$SEGMENT_FILE" | grep -o -P '_[0-9]+\.ts' | tr -dc '0-9' )
+        ENC_FILENAME="$OUTPUT_DIRECTORY/${SEGMENT_PREFIX}_enc_${SEG_NO}".ts
+
+        # Strip leading 0's so printf doesn't think it's octal
+        #SEG_NO=${SEG_NO##+(0)} # Doesn't work for some reason - need to check shopt to look further into it
+        SEG_NO=$(echo $SEG_NO | sed 's/^0*//' )
+        
+        # Convert the segment number to an IV. 
+	INIT_VECTOR=$(printf '%032x' $SEG_NO)
+	openssl aes-128-cbc -e -in $SEGMENT_FILE -out $ENC_FILENAME -nosalt -iv $INIT_VECTOR -K $ENCRYPTION_KEY
+
+        # Move encrypted file to the original filename, so that the m3u8 file does not have to be changed
+        mv $ENC_FILENAME $SEGMENT_FILE
+        
+    done
+
+    echo "Updating Manifests"
+    # this isn't technically correct as we needn't write into the master, but should still work
+    for manifest in ${OUTPUT_DIRECTORY}/*.m3u8
+    do
+        # Insert the KEY at the 5'th line in the m3u8 file
+        sed -i "5i #EXT-X-KEY:METHOD=AES-128,URI="${KEY_PREFIX}${KEY_NAME}.key "$manifest"
+    done
+}
 
 # This is used internally, if the user wants to specify their own flags they should be
-# setting FFMPEG_FLAGS
+# setting FFMPEG_FLAGS or FFMPEG_INPUT_FLAGS
 FFMPEG_ADDITIONAL=''
 LIVE_SEGMENT_COUNT=0
 IS_FIFO=0
 TMPDIR=${TMPDIR:-"/tmp"}
 MYPID=$$
+TWOPASS=false
+QUALITY=
+CONSTANT=false
 # Get the input data
 
 # This exists to maintain b/c
 LEGACY_ARGS=1
 
 # If even one argument is supplied, switch off legacy argument style
-while getopts "i:o:s:c:b:p:t:S:lf" flag
+while getopts "i:o:s:c:b:p:t:S:q:u:k:K:Clfe2" flag
 do
 	LEGACY_ARGS=0
         case "$flag" in
@@ -211,6 +311,13 @@
 		p) PLAYLIST_PREFIX="$OPTARG";;
 		t) SEGMENT_PREFIX="$OPTARG";;
 		S) SEGMENT_DIRECTORY="$OPTARG";;
+		e) ENCRYPT=1;;
+		2) TWOPASS=true;;
+		q) QUALITY="$OPTARG";;
+		C) CONSTANT=true;;
+		u) PATH_PREFIX="$OPTARG";;
+		k) KEY_PREFIX="$OPTARG";;
+		K) KEY_NAME="$OPTARG";;
         esac
 done
 
@@ -251,7 +358,17 @@
 then
   echo "Warning: Input is FIFO - EXPERIMENTAL"
   IS_FIFO=1
-
+fi
+
+# Make sure that the trailing slashes are added
+if [ "$PATH_PREFIX" != "" ] && [ "${PATH_PREFIX:$length:1}" != "/" ]
+then
+  PATH_PREFIX+="/"
+fi
+
+if [ "$KEY_PREFIX" != "" ] && [ "${KEY_PREFIX:$length:1}" != "/" ]
+then
+  KEY_PREFIX+="/"
 fi
 
 # Check output directory exists otherwise create it
@@ -267,7 +384,8 @@
 
     if [ "$LIVE_SEGMENT_COUNT" -gt 0 ]
     then
-	FFMPEG_ADDITIONAL+=" -segment_list_size $LIVE_SEGMENT_COUNT"
+	WRAP_POINT=$(($LIVE_SEGMENT_COUNT * 2)) # Wrap the segment numbering after 2 manifest lengths - prevents disks from filling
+	FFMPEG_ADDITIONAL+=" -segment_list_size $LIVE_SEGMENT_COUNT -segment_wrap $WRAP_POINT"
     fi
 fi
 
@@ -278,6 +396,7 @@
 # If a prefix hasn't been specified, use the input filename
 PLAYLIST_PREFIX=${PLAYLIST_PREFIX:-$INPUTFILENAME}
 SEGMENT_PREFIX=${SEGMENT_PREFIX:-$PLAYLIST_PREFIX}
+KEY_NAME=${KEY_NAME:-$PLAYLIST_PREFIX}
 
 # The 'S' option allows segments and bitrate specific manifests to be placed in a subdir
 SEGMENT_DIRECTORY=${SEGMENT_DIRECTORY:-''}
@@ -291,6 +410,7 @@
 	fi
 
 	SEGMENT_DIRECTORY+="/"
+	OUTPUT_DIRECTORY+="/"
 fi
 
 # Set the bitrate
@@ -307,7 +427,13 @@
       createVariantPlaylist "$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_master.m3u8"
       for br in $OP_BITRATES
       do
-	    appendVariantPlaylistentry "$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_master.m3u8" "${SEGMENT_DIRECTORY}${PLAYLIST_PREFIX}_${br}.m3u8" "$br"
+            bw=$br
+            if [[ "$br" == *"-"* ]]
+            then
+                bw=$(echo "$br" | cut -d- -f1) 
+            fi
+      
+	    appendVariantPlaylistentry "$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_master.m3u8" "${SEGMENT_DIRECTORY}${PLAYLIST_PREFIX}_${bw}.m3u8" "$br"
       done
 
       OUTPUT_DIRECTORY+=$SEGMENT_DIRECTORY
@@ -315,7 +441,29 @@
       # Now for the longer running bit, transcode the video
       for br in $OP_BITRATES
       do
-	      BITRATE="-b:v ${br}k -bufsize ${br}k"
+      
+              # Check whether there's a resolution included in the bitrate string
+              #
+              # See HLS-27
+              if [[ "$br" == *"-"* ]]
+              then
+                resolution="-vf scale=$(echo "$br" | cut -d- -f2 | sed 's/x/:/')"
+                br=$(echo "$br" | cut -d- -f1) 
+              fi
+      
+              if [ -z $QUALITY ]; then
+		if $CONSTANT; then
+	          BITRATE="-b:v ${br}k -bufsize ${br}k -minrate ${br}k -maxrate ${br}k"
+		else
+	          BITRATE="-b:v ${br}k"
+		fi
+	      else
+	        BITRATE="-crf $QUALITY -maxrate ${br}k -bufsize ${br}k"
+                if [ $VIDEO_CODEC = "libx265" ]; then
+                  BITRATE="$BITRATE -x265-params --vbv-maxrate ${br}k --vbv-bufsize ${br}k"
+                fi
+	      fi
+	      echo "Bitrate options: $BITRATE"
 	      # Finally, lets build the output filename format
 	      OUT_NAME="${SEGMENT_PREFIX}_${br}_%05d.ts"
 	      PLAYLIST_NAME="$OUTPUT_DIRECTORY/${PLAYLIST_PREFIX}_${br}.m3u8"
@@ -355,6 +503,9 @@
 	    # Monitor the background tasks for completion
 	    echo "All transcoding processes started, awaiting completion"
 	    awaitCompletion
+	    
+	    # As of HLS-20 encrypt will only run if the relevant vars are set
+	    encrypt
       fi
 
       if [ "$IS_FIFO" == "1" ]
@@ -382,6 +533,7 @@
 
   createStream "$PLAYLIST_NAME" "$OUT_NAME" "$BITRATE" "$INPUTFILE"
 
-
-fi
-
+  # As of HLS-20 encrypt will only run if the relevant vars are set
+  encrypt
+fi
+

file:a/README.md -> file:b/README.md
--- a/README.md
+++ b/README.md
@@ -15,40 +15,37 @@
 
 Usage is incredibly simple
 
-```
-./HLS-Stream-Creator.sh -[lf] [-c segmentcount] -i [inputfile] -s [segmentlength(seconds)] -o [outputdir] -b [bitrates]
-
-
-Deprecated Legacy usage:
+    ./HLS-Stream-Creator.sh -[lf] [-c segmentcount] -i [inputfile] -s [segmentlength(seconds)] -o [outputdir] -b [bitrates]
+
+    Deprecated Legacy usage:
 	HLS-Stream-Creator.sh inputfile segmentlength(seconds) [outputdir='./output']
 
-```
 
 So to split a video file called *example.avi* into segments of 10 seconds, we'd run
 
-```
-./HLS-Stream-Creator.sh -i example.avi -s 10
-```
+    ./HLS-Stream-Creator.sh -i example.avi -s 10
 
 **Arguments**
 
-```
     Mandatory Arguments:
 
 	-i [file]	Input file
-	-s [s]  	Segment length (seconds)
+	-s [s]		Segment length (seconds)
 
     Optional Arguments:
 
 	-o [directory]	Output directory (default: ./output)
 	-c [count]	Number of segments to include in playlist (live streams only) - 0 is no limit
+	-e      	Encrypt the HLS segments (a key will be generated automatically)
 	-b [bitrates]	Output video Bitrates in kb/s (comma seperated list for adaptive streams)
 	-p [name]	Playlist filename prefix
 	-t [name]	Segment filename prefix
 	-l		Input is a live stream
 	-f		Foreground encoding only (adaptive non-live streams only)
 	-S		Name of a subdirectory to put segments into
-```
+	-2		Use two-pass encoding
+	-q [quality]	Change encoding to CFR with [quality]
+	-C		Use constant bitrate as opposed to variable bitrate
 
 
 Adaptive Streams
@@ -58,38 +55,75 @@
 
 In order to create seperate bitrate streams, pass a comma seperated list in with the *-b* option
 
-```
-./HLS-Stream-Creator.sh -i example.avi -s 10 -b 28,64,128,256
-```
+    ./HLS-Stream-Creator.sh -i example.avi -s 10 -b 28,64,128,256
+
 
 By default, transcoding for each bitrate will be forked into the background - if you wish to process the bitrates sequentially, pass the *-f* option
 
-```
-./HLS-Stream-Creator.sh -i example.avi -s 10 -b 28,64,128,256 -f
-```
-
-In either case, in accordance with the HLS spec, the audio bitrate will remain unchanged
-
-
+    ./HLS-Stream-Creator.sh -i example.avi -s 10 -b 28,64,128,256 -f
+
+
+In either case, in accordance with the HLS spec, the audio bitrate will remain unchanged.
+
+#### Multiple Resolutions
+
+As of [HLS-27](https://projects.bentasker.co.uk/jira_projects/browse/HLS-27.html) it is possible to (optionally) specify a resolution as well as the desired bitrate by appending it to the bitrate it applies to:
+
+    ./HLS-Stream-Creator.sh -i example.avi -s 10 -b 128-600x400,256-1280x720,2000
+
+In the example above, the first two bitrates will use their specified resolutions, whilst the last will use whatever resolution the source video uses.
+
+
+The format to use is
+
+    [bitrate]-[width]x[height]
+
+Note: There are currently no checks to ensure the specified resolution isn't larger than the source media, so you'll need to check this yourself (for the time being).
+
+You should [consider the potential ramifications of player behaviour](https://projects.bentasker.co.uk/jira_projects/browse/HLS-27.html#comment2186312) before using this functionality.
+
+
+
+Encrypted Streams
+-------------------
+
+HLS-Stream-Creator can also create encrypted HLS streams, it's enabled by passing *-e*
+
+    ./HLS-Stream-Creator.sh -i example.avi -e -s 10 -b 28,64,128,256
+
+
+The script will generate a 128 bit key and save it to a *.key* file in the same directory as the segments. Each segment will be AES-128 encrypted using an IV which corresponds to it's segment number (the [default behaviour](https://developer.apple.com/library/content/technotes/tn2288/_index.html#//apple_ref/doc/uid/DTS40012238-CH1-ENCRYPT) for HLS).
+
+The manifests will then be updated to include the necessary `EXT-X-KEY` tag:
+
+    #EXTM3U
+    #EXT-X-VERSION:3
+    #EXT-X-MEDIA-SEQUENCE:0
+    #EXT-X-ALLOW-CACHE:YES
+    #EXT-X-KEY:METHOD=AES-128,URI=big_buck_bunny_720p_stereo.avi.key
+    #EXT-X-TARGETDURATION:17
+    #EXTINF:10.500000,
+    big_buck_bunny_720p_stereo.avi_1372_00000.ts
+
+    
 Output
 -------
 
-As of version 1, the HLS resources will be output to the directory *output*. These will consist of video segments encoded in H.264 with AAC audio and an m3u8 file in the format
-
->\#EXTM3U  
->\#EXT-X-MEDIA-SEQUENCE:0  
->\#EXT-X-VERSION:3  
->\#EXT-X-TARGETDURATION:10  
->\#EXTINF:10, no desc  
->example_00001.ts  
->\#EXTINF:10, no desc  
->example_00002.ts  
->\#EXTINF:10, no desc  
->example_00003.ts  
->\#EXTINF:5, no desc  
->example_00004.ts  
->\#EXT-X-ENDLIST
-
+As of version 1, the HLS resources will be output to the directory *output* (unless a different directory has been specified with *-o*). These will consist of video segments encoded in H.264 with AAC audio and an m3u8 file in the format
+
+    #EXTM3U  
+    #EXT-X-MEDIA-SEQUENCE:0  
+    #EXT-X-VERSION:3  
+    #EXT-X-TARGETDURATION:10  
+    #EXTINF:10, no desc  
+    example_00001.ts  
+    #EXTINF:10, no desc  
+    example_00002.ts  
+    #EXTINF:10, no desc  
+    example_00003.ts  
+    #EXTINF:5, no desc  
+    example_00004.ts  
+    #EXT-X-ENDLIST
 
 
 Using a Specific FFMPEG binary
@@ -97,9 +131,40 @@
 
 There may be occasions where you don't want to use the *ffmpeg* that appears in PATH. At the top of the script, the ffmpeg command is defined, so change this to suit your needs
 
-```
-FFMPEG='/path/to/different/ffmpeg'
-```
+    FFMPEG='/path/to/different/ffmpeg'
+
+
+H265 details
+------------
+
+Check has been added for libx265 to enforce bitrate limits for H265 since it uses additional parameters.
+
+
+
+Audio Codec Availability
+--------------------------
+
+Because *libfdk_aac* is a non-free codec, and is not available in all builds, commit 0796feb switched the default audio codec to *aac*.
+
+However, in older versions of ffmpeg, aac was marked as experimental - this includes the packages currently in the repos for Ubuntu Xenial. As a result, when running the script, you may see the following error
+
+    The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it.
+
+
+There are two ways to work around this. If you have the libfdk_aac codec installed, you can specify that it should be used instead
+
+    export AUDIO_CODEC="libfdk_aac"
+
+
+Alternatively, you can update the ffmpeg flags to enable experimental codecs
+
+    export FFMPEG_FLAGS='-strict -2'
+
+And the re-run HLS-Stream-Creator.
+
+[HLS-23](http://projects.bentasker.co.uk/jira_projects/browse/HLS-23.html) will, in future, update the script to check for this automatically.
+
+
 
 
 Additional Environment Variables
@@ -114,11 +179,31 @@
 
 Example usage:
 
-```
-export VIDEO_CODEC="nvenc"
-export FFMPEG_FLAGS="-pix_fmt yuv420p -profile:v"
-./HLS-Stream-Creator.sh example.avi 10
-```
+
+    export VIDEO_CODEC="nvenc"
+    export FFMPEG_FLAGS="-pix_fmt yuv420p -profile:v"
+    ./HLS-Stream-Creator.sh example.avi 10
+
+
+
+OS X Users
+------------
+
+Segment encryption won't work out of the box on OS X as it relies on arguments which the BSD `grep` and `sed` commands don't support. In order to use encryption on OS X you must first install their GNU counterparts
+
+
+    brew install gnu-sed --with-default-names
+    brew install grep --with-default-names
+
+
+Automation
+-----------
+
+HLS-Stream-Creator was originally created as a short bit of research and has since grown significantly. The consequence of this, is that it was primarily focused on being run manually.
+
+Although still not a perfect solution to automation, an example of [automating HLS-Stream-Creator can be found here](https://snippets.bentasker.co.uk/page-1804131128-Automating-HLS-Stream-Creator-Python.html). Better automation support will hopefully be added sometime in the future (pull requests very welcome).
+
+
 
 License
 --------

--- /dev/null
+++ b/output/false_master.m3u8
@@ -1,1 +1,8 @@
+#EXTM3U
+#EXT-X-STREAM-INF:BANDWIDTH=436000
+false_436.m3u8
+#EXT-X-STREAM-INF:BANDWIDTH=128000
+false_128.m3u8
+#EXT-X-STREAM-INF:BANDWIDTH=512000
+false_512.m3u8