# todo: learn to start working in one corner of the screen.
-# todo: get an icecast on li.b8.nz for when i'm away from home.
-
# potential improvement: it might be nice that we could have a tall terminal bug only use
# the top half for a 1080p stream, this is how:
# https://superuser.com/questions/1106674/how-to-add-blank-lines-above-the-bottom-in-terminal
set -eE -o pipefail
trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR
+usage() {
+ cat <<EOF
+Usage: ${0##*/} [OPTIONS] [sysops|tech|staff]
+3 mountpoints: fsf-sysops (default, public), fsf (all staff), fsf-tech (tech team)
+
+-d debug.
+-f Stream full screen even high resolution.
+-t Stream tall half screen
+-u Undelayed. Removes 5 second video delay, and about 4 second audio delay.
+-w do not launch watch of stream
+
+note: args duplicated in ffp
+
+
+-h|--help Print help and exit.
+Note: Uses util-linux getopt option parsing: spaces between args and
+options, short options can be combined, options before args.
+EOF
+ exit $1
+}
+
+##### begin command line parsing ########
+
+# ensure we can handle args with spaces or empty.
+ret=0; getopt -T || ret=$?
+[[ $ret == 4 ]] || { echo "Install util-linux for enhanced getopt" >&2; exit 1; }
+
+ffp_args=()
debug=false
+delay=true
loglevel=fatal
-
-# 3 mountpoints: fsf-sysops (default, public), fsf (all staff), fsf-tech (tech team)
-# # note: duplicated in ffp
-mount_suffix=-sysops
-while [[ $1 ]]; do
+watch=true
+fullscreen=false
+tall=false
+temp=$(getopt -l help hdftuw "$@") || usage 1
+eval set -- "$temp"
+while true; do
case $1 in
- sysops|tech)
- mount_suffix=-$1
- ;;
- staff)
- mount_suffix=
- ;;
-d)
debug=true
loglevel=debug
+ loglevel=info
+ ffp_args+=(-d)
+ ;;
+ -f)
+ fullscreen=true
+ tall=false
+ ;;
+ -t)
+ fullscreen=false
+ tall=true
;;
+ -w)
+ watch=false
+ ;;
+ -u)
+ delay=false
+ ;;
+ -h|--help) usage ;;
+ --) shift; break ;;
+ *) echo "$0: unexpected args: $*" >&2 ; usage 1 ;;
esac
shift
done
+mount_suffix=-sysops
+case $1 in
+ sysops|tech)
+ mount_suffix=-$1
+ ;;&
+ tech)
+ delay=false
+ ;;
+ staff)
+ mount_suffix=
+ ;;
+esac
+
+if $delay; then
+ # 2500 gets us around a 4 second delay, up from 1.5s.
+ delay_arg=,tpad=start_duration=2500ms
+fi
+
+
+##### end command line parsing ########
+
host=live.iankelling.org:8000
-if [[ $(dig +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]] \
- && ip n show 10.2.0.1 | grep . &>/dev/null; then
+if ip n show 10.2.0.1 | grep . &>/dev/null && \
+ [[ $(dig +timeout=1 +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]]; then
host=127.0.0.1:8000
fi
# example xrandr output: 1280x800+0+0
primary_res=$(awk '$2 == "connected" && $3 == "primary" { print $4 }' $tmpf | sed 's/+.*//')
-secondary_res=$(awk '$2 == "connected" && $3 != "primary" { print $3 }' $tmpf | sed 's/+.*//')
+tmp=$(awk '$2 == "connected" && $3 != "primary" { print $3 }' $tmpf | sed 's/+/ /g')
+read -r secondary_res x_offset _ <<<"$tmp"
+
if [[ $secondary_res ]]; then
- # assumes secondary is on the right
- x_offset=${primary_res%%x*}
secondary_x=${secondary_res%%x*}
secondary_y=${secondary_res##*x}
- stream_res=$(( secondary_x / 2 ))x$(( secondary_y / 2))
+ if $fullscreen; then
+ stream_res=$secondary_res
+ elif $tall; then
+ stream_res=$(( secondary_x / 2 ))x$secondary_y
+ else
+ stream_res=$(( secondary_x / 2 ))x$(( secondary_y / 2))
+ fi
else
x_offset=0
stream_res=$primary_res
# Monitor of default sink.
# eg: alsa_output.usb-Audio-gd_Audio-gd-00.analog-stereo
-pa_sink=$(pacmd list-sinks | awk '/\*/ {getline; print $2}' | sed 's/^<//;s/>$//').monitor
+pa_sink=$(pactl get-default-sink).monitor
+
+# this is for ffmpeg warnings. doesnt seem to affect latency.
+# 160 was too small. at 300, it occasionally complains,
+# probably only when we are using delayed output
+thread_queue_size_arg="-thread_queue_size 500"
opts=(
# global options
-hide_banner
-nostats
+ # tested for decreasing latency: did not help.
+ # -probesize 32
+ # tested for warning "Queue input is backward in time". did not help.
+ #-rtbufsize 500M
+
# note: ordering of inputs also affects zmqsend commands.
## audio input options
-f pulse
-name ffs
- # note: duplicated above
- -thread_queue_size 160
+ # note: duplicated
+ $thread_queue_size_arg
-fragment_size 512
-i default
-f pulse
- # this is for ffmpeg warnings. doesnt seem to affect latency.
- -thread_queue_size 160
+ $thread_queue_size_arg
# pulse knows this name somewhere
-name ffsdesktop
# This fixes latency. i haven't tried tuning it, but going too low creates
## video input options
-video_size $stream_res
+ $thread_queue_size_arg
-f x11grab
-framerate $framerate
-i :0.0+$x_offset.0
# localhost url caused an error for me.
-filter_complex "[0]azmq,volume=precision=fixed: volume=0 [vol0];
[1]azmq='b=tcp\://127.0.0.1\:5556',volume=precision=fixed: volume=0 [vol1];
-[vol0][vol1] amerge=inputs=2 [out];
-[2]zmq='b=tcp\://127.0.0.1\:5557',drawbox=color=0x262626,drawtext=fontsize=90: fontcolor=beige: x=40: y=40: text=''"
+[vol0][vol1] amerge=inputs=2;
+[2]zmq='b=tcp\://127.0.0.1\:5557',drawbox=color=0x262626,drawtext=fontsize=90: fontcolor=beige: x=40: y=40: text=''${delay_arg}[out]"
+
+# [vol0][vol1] amerge=inputs=2,adelay=6000:all=1;
+
+
+ # An online source says to match a 5 second vid delay, we can do an
+ # audio delay filter: "adelay=5000|5000". However, we already get
+ # a stream delay of about 2 seconds, and having the audio be about
+ # 2 seconds ahead is fine, they do that intentionally in soccer
+ # matches.
# Based on error message and poking around, it seems ffmpeg is not
# smart enough to see that [vol0] and [vol1] are inputs to the amerge
# for 1080p, default 256k is poor quality. 500 is ok. 1500 is a bit better.
-b:v 1500k
-threads 2
- -buffer_duration 10
-error-resilient 1
## audio output options
# start muted
pactl set-source-mute @DEFAULT_SOURCE@ true
+if pkill -f ^ffmpeg.\*icecast://source.\*/fsf; then
+ sleep 1
+fi
+
#echo executing: ffmpeg ${opts[@]}
#{ sleep 1; ffp &>/dev/null & }
exit 0
fi
-# For now, we want to watch the stream and end the stream when we stop watching.
+##### begin clipboard history checkup ####
+
+# Avoid streaming with secrets in our clipboard history. We could just
+# clear the history, but here I truncate it to a max and then show it,
+# and then I can press super+y if I want to clear it, or close the
+# window if I want to keep it.
+copyqcount=$(copyq count)
+regex='^[1-9][0-9]*$'
+if [[ $copyqcount =~ $regex ]]; then
+ # i dont want to think about more than this
+ max_rows=40
+ if (( copyqcount >= max_rows )); then
+ rows_arg=()
+ for ((i=max_rows; i<copyqcount; i++)); do
+ rows_arg+=($i)
+ done
+ copyq remove "${rows_arg[@]}"
+ fi
+ copyq show
+ gone=false
+ for (( i=0; i<40; i++ )); do
+ if i3-msg -t get_tree | jq -e '.. | select(.class? == "copyq" and .instance? == "copyq")' &>/dev/null; then
+ sleep .5
+ else
+ gone=true
+ break
+ fi
+ done
+ if ! $gone; then
+ msg="ffs: copyq not gone. aborting. super+y = copyq-restart / clear"
+ if [[ -t 0 ]]; then
+ echo $msg
+ else
+ dunstify -u critical -h string:x-dunst-stack-tag:alert "$msg"
+ fi
+ exit 1
+ fi
+fi
+##### end clipboard history checkup ####
+
+if [[ $mount_suffix == -sysops ]]; then
+ touch $HOME/.iank-stream-on
+fi
+
+echo true >$HOME/.iank-stream-muted
+
ffmpeg "${opts[@]}" &
-sleep 2
-ffp ||:
-kill %%
+if $watch; then
+ # watch the stream and end the stream when we stop watching.
+ sleep 2
+ ffp -d "${ffp_args[@]}" ||:
+ kill %%
+ rm -f $HOME/.iank-stream-on
+fi