aboutsummaryrefslogtreecommitdiff
path: root/.scripts/ipcam
diff options
context:
space:
mode:
Diffstat (limited to '.scripts/ipcam')
-rwxr-xr-x.scripts/ipcam526
1 files changed, 526 insertions, 0 deletions
diff --git a/.scripts/ipcam b/.scripts/ipcam
new file mode 100755
index 0000000..3bc6d73
--- /dev/null
+++ b/.scripts/ipcam
@@ -0,0 +1,526 @@
+#!/bin/bash
+
+### FUNCTIONS
+
+show_help() {
+ echo "Usage:"
+ echo " $0 [options]"
+ echo
+ echo "Script for using IP Webcam as a microphone/webcam."
+ echo
+ echo "Options:"
+ echo " -a, --audio capture only audio"
+ echo " -b, --adb-path <path> set adb location if not in PATH"
+ echo " -C, --no-echo-cancel do not set up echo cancellation"
+ echo " -d, --device <device> force video device to use"
+ echo " -f, --flip <flip> flip image"
+ echo " -h, --height <height> set image height (default 480)"
+ echo " -l, --adb-flags <id> adb flags to specify device id"
+ echo " -i, --use-wifi <ip> use wi-fi mode with specified ip"
+ echo " -p, --port <port> port on which IP Webcam is listening (default 8080)"
+ echo " -P, --password <pass> password for accesing the IP Webcam"
+ echo " -s, --no-sync No force syncing to timestamps"
+ echo " -t, --with-tee adds the 'tee' multiplexer to the pipeline, to workaround 'single-frame' capture issue (see issue #97)"
+ echo " -u, --username <user> username for accesing the IP Webcam"
+ echo " -v, --video capture only video"
+ echo " -w, --width <width> set image width (default 640)"
+ echo " -x, --no-proxy disable proxy while acessing IP"
+ echo " --help show this help"
+}
+check_os_version() {
+ # checks if the OS version can use newer GStreamer version
+ DIST="$1"
+ RELEASE="$2"
+
+ case "$DIST" in
+ "Debian") return "$(echo "$RELEASE < 8.0" | bc)" ;;
+ "Ubuntu") return "$(echo "$RELEASE < 14.04" | bc)" ;;
+ "LinuxMint") return "$(echo "$RELEASE < 14.04" | bc)" ;;
+ "Arch") return 0 ;;
+ esac
+ # assume other Distributions are also new enough, by now
+ return 0
+}
+
+error() {
+ zenity --error --no-wrap --text "$@" >/dev/null 2>&1
+ exit 1
+}
+
+warning() {
+ zenity --warning --no-wrap --text "$@" >/dev/null 2>&1
+}
+
+info() {
+ zenity --info --no-wrap --text "$@" >/dev/null 2>&1
+}
+
+confirm() {
+ zenity --question --no-wrap --text "$@" >/dev/null 2>&1
+}
+
+can_run() {
+ # It's either the path to a file, or the name of an executable in $PATH
+ which "$1" >/dev/null 2>/dev/null
+}
+
+start_adb() {
+ can_run "$ADB" && "$ADB" $ADB_FLAGS start-server
+}
+
+phone_plugged() {
+ test "$("$ADB" $ADB_FLAGS get-state 2>/dev/null)" = "device"
+}
+
+url_reachable() {
+ CURL_OPTIONS=""
+ if [ $DISABLE_PROXY = 1 ]; then
+ CURL_OPTIONS+="--noproxy $IP"
+ fi
+
+ if [[ $USERNAME != "" && $PASSWORD != "" ]]; then
+ CURL_OPTIONS+="-u $USERNAME:$PASSWORD"
+ fi
+ # -f produces a non-zero status code when answer is 4xx or 5xx
+ curl $CURL_OPTIONS -f -m 5 -sI "$1" >/dev/null
+}
+
+iw_server_is_started() {
+ if [ $CAPTURE_STREAM = av ]; then
+ : # help me optimize this code
+ temp=$(url_reachable "$AUDIO_URL")
+ au=$? #echo au=$au
+ temp=$(url_reachable "$VIDEO_URL")
+ vu=$? #echo vu=$vu
+ if [ $au = 0 -a $vu = 0 ]; then return 0; else return 1; fi
+ elif [ $CAPTURE_STREAM = a ]; then
+ if url_reachable "$AUDIO_URL"; then return 0; else return 1; fi
+ elif [ $CAPTURE_STREAM = v ]; then
+ if url_reachable "$VIDEO_URL"; then return 0; else return 1; fi
+ else
+ error "Incorrect CAPTURE_STREAM value ($CAPTURE_STREAM). Should be a, v or av."
+ fi
+}
+
+module_id_by_sinkname() {
+ pactl list sinks | grep -e "Name:" -e "Module:" | grep -A1 "Name: $1$" | grep Module: | cut -f2 -d: | tr -d ' '
+}
+
+### CONFIGURATION
+
+# Exit on first error
+set -e
+
+# Choose which stream to capture.
+# a - audio only, v - video only, av - audio and video.
+# Make sure that IP webcam is streaming corresponding streams, otherwise error will occur.
+# Defaults to audio and video, ovverrided by command line options.
+CAPTURE_STREAM=av
+
+# Choose audio codec from wav, aac or opus
+# do not choose opus until editing pipeline. If choose opus, pipeline will not work
+# and some errors will appear in feed.log.
+# I do not know how to edit pipelines for now.
+AUDIO_CODEC=wav
+
+# Port on which IP Webcam is listening
+# Defaults to 8080, ovverrided by command line options.
+PORT=8080
+
+# If your "adb" is not in your $PATH, specify it on command line.
+if can_run adb; then ADB=$(which adb); fi
+
+# Flags for ADB.
+# when you need to pick from several devices, specify deviceid on command line (list deviceids with 'adb devices').
+ADB_FLAGS=
+
+# set on command line
+FLIP_METHOD=
+
+# Default dimensions of video, can be overridden on command line.
+# Make sure both dimensions are multiples of 16 (see issue #97).
+WIDTH=640
+HEIGHT=480
+
+# Force syncing to timestamps. Useful to keep audio and video in sync,
+# but may impact performance in slow connections. If you see errors about
+# timestamping or you do not need audio, you can try changing this to false from command line.
+SYNC=true
+
+# To disable proxy while acessing IP (set value 1 to disable, 0 for not)
+# For cases when host m/c is connected to a Proxy-Server and IP belongs to local network
+DISABLE_PROXY=0
+
+# To disable echo cancellation
+DISABLE_ECHO_CANCEL=0
+
+# Use exclusive caps by default (required by Chrome, Cheese and others)
+V4L2_OPTS="exclusive_caps=1"
+
+USERNAME=""
+PASSWORD=""
+
+OPTS=$(getopt -o ab:Cd:f:h:l:i:p:P:stu:vw:x --long audio,adb-path:,no-echo-cancel,device:,flip:,height:,help,adb-flags:,use-wifi:,port:,password:,no-sync,with-tee,username:,video,width:,no-proxy -n "$0" -- "$@")
+eval set -- "$OPTS"
+
+while true; do
+ case "$1" in
+ -a | --audio)
+ CAPTURE_STREAM="a"
+ shift
+ ;;
+ -b | --adb-path)
+ ADB="$2"
+ shift 2
+ ;;
+ -C | --no-echo-cancel)
+ DISABLE_ECHO_CANCEL=1
+ shift
+ ;;
+ -d | --device)
+ DEVICE="$2"
+ shift 2
+ ;;
+ -f | --flip)
+ FLIP_METHOD="$2"
+ shift 2
+ ;;
+ -h | --height)
+ HEIGHT="$2"
+ shift 2
+ ;;
+ -l | --adb-flags)
+ ADB_FLAGS="-s $2"
+ shift 2
+ ;;
+ -i | --use-wifi)
+ IP="$2"
+ shift 2
+ ;;
+ -p | --port)
+ PORT="$2"
+ shift 2
+ ;;
+ -P | --password)
+ PASSWORD="$2"
+ shift 2
+ ;;
+ -u | --username)
+ USERNAME="$2"
+ shift 2
+ ;;
+ -s | --no-sync)
+ SYNC=false
+ shift
+ ;;
+ -t | --with-tee)
+ USE_TEE=true
+ shift
+ ;;
+ -v | --video)
+ CAPTURE_STREAM="v"
+ shift
+ ;;
+ -w | --width)
+ WIDTH="$2"
+ shift 2
+ ;;
+ -x | --no-proxy)
+ DISABLE_PROXY=1
+ shift
+ ;;
+ --help)
+ show_help
+ exit
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo "Internal error!"
+ exit 1
+ ;;
+ esac
+done
+
+declare -A DISTS
+DISTS=(["Debian"]=1 ["Ubuntu"]=2 ["Arch"]=3 ["LinuxMint"]=4)
+
+if can_run lsb_release; then
+ DIST=$(lsb_release -i | cut -f2 -d ":")
+ RELEASE=$(lsb_release -r | cut -f2 -d ":")
+fi
+if [ -z "$DIST" ] || [ -z "${DISTS[$DIST]}" ]; then
+ if [ -f "/etc/arch-release" ]; then
+ DIST="Arch"
+ RELEASE=""
+ elif [ -f "/etc/debian_version" ]; then
+ DIST="Debian"
+ RELEASE=$(perl -ne 'chomp; if(m:(jessie|testing|sid):){print "8.0"}elsif(m:[\d\.]+:){print}else{print "0.0"}' </etc/debian_version)
+ fi
+fi
+
+GST_VER="0.10"
+GST_VIDEO_CONVERTER="ffmpegcolorspace"
+GST_VIDEO_MIMETYPE="video/x-raw-yuv"
+GST_VIDEO_FORMAT="format=(fourcc)YUY2"
+
+GST_AUDIO_MIMETYPE="audio/x-raw-int"
+GST_AUDIO_FORMAT="width=16,depth=16,endianness=1234,signed=true"
+GST_AUDIO_RATE="rate=44100"
+GST_AUDIO_CHANNELS="channels=1"
+GST_AUDIO_LAYOUT=""
+
+set +e
+check_os_version $DIST $RELEASE
+set -e
+if [ $? -eq 0 ]; then
+ GST_VER="1.0"
+ GST_VIDEO_CONVERTER="videoconvert"
+ GST_VIDEO_MIMETYPE="video/x-raw"
+ GST_VIDEO_FORMAT="format=YUY2"
+
+ GST_AUDIO_MIMETYPE="audio/x-raw"
+ GST_AUDIO_FORMAT="format=S16LE"
+ GST_AUDIO_LAYOUT=",layout=interleaved"
+fi
+
+DIMENSIONS="width=$WIDTH,height=$HEIGHT"
+
+GST_VIDEO_CAPS="$GST_VIDEO_MIMETYPE,$GST_VIDEO_FORMAT,$DIMENSIONS"
+GST_AUDIO_CAPS="$GST_AUDIO_MIMETYPE,$GST_AUDIO_FORMAT$GST_AUDIO_LAYOUT,$GST_AUDIO_RATE,$GST_AUDIO_CHANNELS"
+PA_AUDIO_CAPS="$GST_AUDIO_FORMAT $GST_AUDIO_RATE $GST_AUDIO_CHANNELS"
+
+# GStreamer debug string (see gst-launch manpage)
+GST_DEBUG=souphttpsrc:0,videoflip:0,$GST_CONVERTER:0,v4l2sink:0,pulse:0
+# Is $GST_CONVERTER defined anywhere? Maybe you mean videoconvert vs ffmpegcolorspace? It is in GST_VIDEO_CONVERTER
+
+### MAIN BODY
+
+# Probe module if not probed yet
+if lsmod | grep -w v4l2loopback >/dev/null 2>/dev/null; then
+ # module is already loaded, do nothing
+ :
+elif [ $CAPTURE_STREAM = v -o $CAPTURE_STREAM = av ]; then
+ if can_run sudo; then
+ echo Loading module
+ sudo modprobe v4l2loopback $V4L2_OPTS #-q > /dev/null 2>&1
+ sleep .05
+ else
+ echo Load module with \"modprobe v4l2loopback $V4L2_OPTS\"
+ fi
+fi
+
+# If the user hasn't manually specified which /dev/video* to use
+# through DEVICE, use the first "v4l2 loopback" device as the webcam:
+# this should help when loading v4l2loopback on a system that already
+# has a regular webcam. If that doesn't work, fall back to /dev/video0.
+if [ -z "$DEVICE" ]; then
+ if can_run v4l2-ctl; then
+ for d in /dev/video*; do
+ if v4l2-ctl -d "$d" -D | grep -q "v4l2 loopback"; then
+ DEVICE=$d
+ break
+ fi
+ done
+ fi
+ if [ -z "$DEVICE" ]; then
+ DEVICE=/dev/video0
+ warning "Could not find the v4l2loopback device: falling back to $DEVICE"
+ fi
+fi
+
+# Test that we can read from and write to the device
+if ! test -r "$DEVICE"; then
+ error "$DEVICE is not readable: please fix your permissions"
+fi
+if ! test -w "$DEVICE"; then
+ error "$DEVICE is not writable: please fix your permissions"
+fi
+
+# Decide whether to connect through USB or through wi-fi
+if [ -z $IP ]; then
+ # start adb daemon to avoid relaunching it in while
+ if ! can_run "$ADB"; then
+ error "adb is not available: you'll have to use Wi-Fi, which will be slower.\nNext time, please install the Android SDK from developer.android.com/sdk or install adb package."
+ fi
+ start_adb
+ if ! phone_plugged; then
+ error "adb is available, but the phone is not plugged in.\nConnect your phone to USB and allow usb debugging under developer settings or use Wi-Fi (slower)."
+ fi
+ if ss -ln src ":$PORT" | grep -q ":$PORT"; then
+ PIDOF_ADB="$(pidof adb)"
+ if test -n "$PIDOF_ADB" && ss -lptn src ":$PORT" | grep -q "pid=${PIDOF_ADB}"; then
+ if confirm "Your port $PORT seems to be in use by ADB: would you like to clear the previous port before continuing?"; then
+ adb forward --remove tcp:$PORT
+ else
+ exit 1
+ fi
+ else
+ error "Your port $PORT seems to be in use: try using Wi-Fi.\nIf you would like to use USB forwarding, please free it up and try again."
+ fi
+ fi
+ "$ADB" $ADB_FLAGS forward tcp:$PORT tcp:$PORT
+ IP=127.0.0.1
+ MODE=adb
+else
+ MODE=wifi
+fi
+
+BASE_URL=http://$IP:$PORT
+VIDEO_URL=$BASE_URL/videofeed
+AUDIO_URL=$BASE_URL/audio.$AUDIO_CODEC
+
+if ! iw_server_is_started; then
+ if [ $CAPTURE_STREAM = av ]; then
+ MESSAGE="The IP Webcam audio feed is not reachable at <a href=\"$AUDIO_URL\">$AUDIO_URL</a>.\nThe IP Webcam video feed is not reachable at <a href=\"$VIDEO_URL\">$VIDEO_URL</a>."
+ elif [ $CAPTURE_STREAM = a ]; then
+ MESSAGE="The IP Webcam audio feed is not reachable at <a href=\"$AUDIO_URL\">$AUDIO_URL</a>."
+ elif [ $CAPTURE_STREAM = v ]; then
+ MESSAGE="The IP Webcam video feed is not reachable at <a href=\"$VIDEO_URL\">$VIDEO_URL</a>."
+ else
+ error "Incorrect CAPTURE_STREAM value ($CAPTURE_STREAM). Should be a, v or av."
+ fi
+ error "$MESSAGE\nPlease install and open IP Webcam in your phone and start the server.\nMake sure that values of variables IP, PORT, CAPTURE_STREAM in this script are equal with settings in IP Webcam."
+fi
+
+# Test if audio server is running on pipewire
+if pactl info | grep -q PipeWire; then
+ if [ "$CAPTURE_STREAM" = v ]; then
+ :
+ else
+ # currently setting audio sinks errors out, so give user a workaround
+ error "Only video streams on Pipewire are currently supported. Audio is a WIP. Please set CAPTURE_STREAM value to v."
+ fi
+fi
+
+if [ $CAPTURE_STREAM = a -o $CAPTURE_STREAM = av ]; then
+ # idea: check if default-source is correct. If two copy of script are running,
+ # then after ending first before second you will be set up with $SINK_NAME.monitor,
+ # but not with your original defauld source.
+ # The same issue if script was not end correctly, and you restart it.
+ DEFAULT_SINK=$(pactl info | grep "Sink" | cut -f3 -d " ")
+ DEFAULT_SOURCE=$(pactl info | grep "Source" | cut -f3 -d " ")
+
+ SINK_NAME="ipwebcam"
+ SINK_ID=$(module_id_by_sinkname $SINK_NAME)
+ ECANCEL_ID=$(module_id_by_sinkname "${SINK_NAME}_echo_cancel")
+
+ # Registering audio device if not yet registered
+ if [ -z $SINK_ID ]; then
+ SINK_ID=$(pactl load-module module-null-sink \
+ sink_name="$SINK_NAME" \
+ $PA_AUDIO_CAPS \
+ sink_properties="device.description='IP\ Webcam'")
+ fi
+
+ if [ $DISABLE_ECHO_CANCEL = 1 ]; then
+ :
+ elif [ -z $ECANCEL_ID ]; then
+ ECANCEL_ID=$(pactl load-module module-echo-cancel \
+ sink_name="${SINK_NAME}_echo_cancel" \
+ source_master="$SINK_NAME.monitor" \
+ sink_master="$DEFAULT_SINK" \
+ $PA_AUDIO_CAPS \
+ aec_method="webrtc" save_aec=true use_volume_sharing=true) || true
+ fi
+
+ pactl set-default-source $SINK_NAME.monitor
+fi
+
+# Check for gst-launch
+GSTLAUNCH=gst-launch-${GST_VER}
+if ! can_run "$GSTLAUNCH"; then
+ error "Could not find gst-launch. Exiting."
+fi
+
+# Start the GStreamer graph needed to grab the video and audio
+set +e
+
+#sudo v4l2loopback-ctl set-caps $GST_0_10_VIDEO_CAPS $DEVICE
+
+pipeline_video() {
+ GST_FLIP=""
+ GST_TEE=""
+ if [ $FLIP_METHOD ]; then
+ GST_FLIP="! videoflip method=\"$FLIP_METHOD\" "
+ fi
+ # Due to what seems an issue between v4l2loopback and gst-1.0 (https://github.com/umlaeute/v4l2loopback/issues/83),
+ # the pipeline might need the "tee" multiplexer to enforce an additional buffer copy.
+ # Here we enable it if the user explicitly asked to.
+ # TODO: consider automatically enabling this if using gst-1.0 and specific version ranges of v4l2loopback.
+ if [ "$USE_TEE" = "true" ]; then
+ GST_TEE="! tee "
+ fi
+
+ echo souphttpsrc location="$VIDEO_URL" do-timestamp=true is-live=true user-id="$USERNAME" user-pw="$PASSWORD" \
+ ! queue \
+ ! multipartdemux \
+ ! decodebin \
+ $GST_FLIP \
+ ! $GST_VIDEO_CONVERTER \
+ ! videoscale \
+ ! $GST_VIDEO_CAPS \
+ $GST_TEE \
+ ! v4l2sink device="$DEVICE" sync=$SYNC
+}
+
+pipeline_audio() {
+ echo souphttpsrc location="$AUDIO_URL" do-timestamp=true is-live=true user-id="$USERNAME" user-pw="$PASSWORD" \
+ ! $GST_AUDIO_CAPS ! queue \
+ ! pulsesink device="$SINK_NAME" sync=$SYNC
+}
+
+if [ $CAPTURE_STREAM = av ]; then
+ PIPELINE="$(pipeline_audio) $(pipeline_video)"
+elif [ $CAPTURE_STREAM = a ]; then
+ PIPELINE=$(pipeline_audio)
+elif [ $CAPTURE_STREAM = v ]; then
+ PIPELINE=$(pipeline_video)
+else
+ error "Incorrect CAPTURE_STREAM value ($CAPTURE_STREAM). Should be a, v or av."
+fi
+
+# echo "$PIPELINE"
+
+if [ $DISABLE_PROXY = 1 ]; then
+ # Disabling proxy to access WIFI_IP viz. on local network
+ unset http_proxy
+fi
+
+"$GSTLAUNCH" -e -vt --gst-plugin-spew \
+ --gst-debug="$GST_DEBUG" \
+ $PIPELINE \
+ >feed.log 2>&1 &
+# Maybe we need edit this pipeline to transfer it to "Monitor of IP Webcam" to be able to use it as a microphone?
+
+GSTLAUNCH_PID=$!
+
+if [ $CAPTURE_STREAM = av ]; then
+ MESSAGE="IP Webcam audio is streaming through pulseaudio sink '$SINK_NAME'.\nIP Webcam video is streaming through v4l2loopback device $DEVICE.\n"
+elif [ $CAPTURE_STREAM = a ]; then
+ MESSAGE="IP Webcam audio is streaming through pulseaudio sink '$SINK_NAME'.\n"
+elif [ $CAPTURE_STREAM = v ]; then
+ MESSAGE="IP Webcam video is streaming through v4l2loopback device $DEVICE.\n"
+else
+ error "Incorrect CAPTURE_STREAM value ($CAPTURE_STREAM). Should be a, v or av."
+fi
+info "${MESSAGE}You can now open your videochat app."
+
+echo "Press enter to end stream"
+read
+
+kill $GSTLAUNCH_PID >/dev/null 2>&1 || echo ""
+if [ $CAPTURE_STREAM = a -o $CAPTURE_STREAM = av ]; then
+ pactl set-default-source ${DEFAULT_SOURCE}
+ pactl unload-module ${ECANCEL_ID}
+ pactl unload-module ${SINK_ID}
+fi
+
+# Remove the port forwarding, to avoid issues on the next run
+if [ $MODE = adb ]; then "$ADB" $ADB_FLAGS forward --remove tcp:$PORT; fi
+
+echo "Disconnected from IP Webcam. Have a nice day!"
+# idea: capture ctrl-c signal and set default source back