Skip to content

Instantly share code, notes, and snippets.

@bertm
Last active February 8, 2021 20:02
Show Gist options
  • Save bertm/69b05cac7ae2121ed700 to your computer and use it in GitHub Desktop.
Save bertm/69b05cac7ae2121ed700 to your computer and use it in GitHub Desktop.
Freenet M3U8 video stream generator
#!/bin/bash
# This script uses x264 and ffmpeg to recode an input video to low-bitrate MPEG-TS segmented h264
# stream that is suitable for streaming over the web and can be inserted into Freenet.
# Settings have been chosen to strongly prefer quality over encoding speed under conditions of an
# extremely low bitrate, to make the result suitable for direct streaming over Freenet.
#
# (c) bertm, 2014
#
# Prefix for file names in the playlist
# Replace the SSK placeholder below with the proper destination SSK (empty for non-Freenet use)
PRE="http://localhost:8888/SSK@replace-me-with-a-proper-ssk-key/and-meta-dir/"
# Postfix for file names in the playlist (empty for non-Freenet use)
POST="?forcedownload=true"
# Input file
INFILE=big_buck_bunny_480p_stereo.ogg
# Output directory (playlist.m3u8 and segmentXXXX.ts are placed there)
OUTDIR=output
# Output video bitrate (in kbit/s)
VIDEO_KBITRATE=252
# Output audio bitrate (in kbit/s)
AUDIO_KBITRATE=48
# Output audio sample rate (in kHz)
AUDIO_KSAMPRATE=32
mkdir -p "$OUTDIR" || exit 1
rm -f "$OUTDIR/"segment????.ts
frate=$(($(ffprobe -print_format compact -show_streams -- "$INFILE" 2>/dev/null \
| grep "|codec_type=video" | sed -n 's/^stream.*|r_frame_rate=\([0-9\/]\+\).*/\1/p')))
if [ $frate -gt 24 ]
then
int=250
else
int=$(($frate * 10))
fi
video=$(tempfile).mp4
stats=$(tempfile).log
function cleanup_x264() {
rm "$video" "$stats"
}
function fail_x264() {
cleanup_x264
exit 1
}
echo "Encoding [PASS 1/2]"
x264 --preset veryslow --bitrate $VIDEO_KBITRATE --aq-mode 2 --level 4.2 --keyint $int \
--min-keyint $int --no-scenecut --vbv-maxrate $(($VIDEO_KBITRATE * 11 / 10)) \
--vbv-bufsize $VIDEO_KBITRATE --no-fast-pskip --slow-firstpass --pass 1 \
--output "$video" --stats "$stats" --log-level warning "$INFILE" || fail_x264
echo "Encoding [PASS 2/2]"
x264 --preset veryslow --bitrate $VIDEO_KBITRATE --aq-mode 2 --level 4.2 --keyint $int \
--min-keyint $int --no-scenecut --vbv-maxrate $(($VIDEO_KBITRATE * 11 / 10)) \
--vbv-bufsize $VIDEO_KBITRATE --no-fast-pskip --slow-firstpass --pass 2 \
--output "$video" --stats "$stats" --log-level warning "$INFILE" || fail_x264
echo "Encoding [SEGMENT]"
ffmpeg -i "$INFILE" -i "$video" -map 1:0 -map 0:1 -c:v copy -c:a aac -ab ${AUDIO_KBITRATE}k \
-ar ${AUDIO_KSAMPRATE}k -strict experimental -f segment -segment_time 10 \
-segment_format mpegts -bsf:v h264_mp4toannexb -v warning "$OUTDIR/"segment%04d.ts \
|| fail_x264
cleanup_x264
playlist=playlist.m3u8
cd "$OUTDIR"
echo "#EXTM3U" > "$playlist"
echo "#EXT-X-VERSION:3" >> "$playlist"
echo "#EXT-X-ALLOW-CACHE:YES" >> "$playlist"
echo "#EXT-X-PLAYLIST-TYPE:VOD" >> "$playlist"
probes=$(tempfile)
for file in segment????.ts
do
ffprobe -print_format compact -show_format -- "$file" 2>/dev/null \
| sed -n 's/^format.*|filename=\([^|]\+\).*|duration=\([^|]*\).*/\1|\2/p' >> "$probes"
done
echo -n "#EXT-X-TARGETDURATION:" >> "$playlist"
awk -F\| -- 'BEGIN { max = 0 } { if ($2 > max) { max = $2 } } END { print int(max + 0.5) }' \
"$probes" >> "$playlist"
awk -F\| -v pre="$PRE" -v post="$POST" -- '{ print "#EXTINF:" $2 ","; print pre $1 post }' \
"$probes" >> "$playlist"
echo "#EXT-X-ENDLIST" >> "$playlist"
rm "$probes"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment