gst-screencast.sh: flush on EOS to make videos usable in video editors
[experiments/gstreamer.git] / shell / gst-screencast.sh
index 1902f49..e601a62 100755 (executable)
 #!/bin/sh
 #!/bin/sh
-
+#
 # gst-screencast - screencasting of a window using GStreamer
 #
 # gst-screencast - screencasting of a window using GStreamer
 #
-# Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
+# Copyright (C) 2015-2017  Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
 #
 #
-# This program is free software. It comes without any warranty, to
-# the extent permitted by applicable law. You can redistribute it
-# and/or modify it under the terms of the Do What The Fuck You Want
-# To Public License, Version 2, as published by Sam Hocevar. See
-# http://sam.zoy.org/wtfpl/COPYING for more details.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 set -e
 
 set -e
-set -x
 
 
-[ "x" = "x$1" ] && { echo "usage: $(basename $0) <output_file> [<ximagesrc_options>]" 1>&2; exit 1; }
+usage() {
+  cat <<EOF
+usage: $(basename $0) [OPTIONS] <output_file> [<ximagesrc_options>]
+
+Screencast script based on GStreamer.
+
+Options:
+  --audio         record audio from pulsesrc
+  --frame         include the window manager border, without the shadows
+  --pointer       capture the mouse pointer
+  --single-shot   capture a single snapshot as a PNG instead of a video
+  -h, --help      display this usage message and exit
+
+EOF
+}
+
+CAPTURE_AUDIO=0
+WINDOW_FRAME=0
+SHOW_POINTER=0
+SINGLE_SHOT=0
+
+while [ $# -gt 0 ];
+do
+  case "$1" in
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    --audio)
+      CAPTURE_AUDIO=1
+      ;;
+    --frame)
+      WINDOW_FRAME=1
+      ;;
+    --pointer)
+      SHOW_POINTER=1
+      ;;
+    --single-shot)
+      SINGLE_SHOT=1
+      ;;
+    -*)
+      echo "Error: Unknown option '${1}'" 1>&2
+      ;;
+    *)
+      break;
+  esac
+  shift
+done
 
 
-[ -f "$1" ] && { echo "ERROR: file already exists!" 1>&2; exit 2; }
+[ "x" = "x$1" ] && { usage 1>&2; exit 1; }
+
+[ -f "$1" ] && { echo "Error: file '${1}' already exists!" 1>&2; exit 2; }
 
 FILENAME="$1"
 
 FILENAME="$1"
+shift
 
 
-if [ "x" = "x$2" ];
+if [ "x" = "x$1" ];
 then
 then
-  # uncomment to capure the whole frame with window manager border and decorations
-  XWININFO_OPTIONS="-frame"
+  SHADOW_SIZE=0
+  SHADOW_X_OFFSET=0
+  SHADOW_Y_OFFSET=0
+  if [ $WINDOW_FRAME -eq 1 ];
+  then
+    XWININFO_OPTIONS="-frame"
 
 
-  WIN_INFO="$(xwininfo $XWININFO_OPTIONS)"
+    # XXX the values below are still hardcoded, they may be different
+    # depending on the theme and the window manager.
+    SHADOW_SIZE=26
+    SHADOW_X_OFFSET=0
+    SHADOW_Y_OFFSET=3
+  fi
 
 
-  #XID=$(echo "$WIN_INFO" | grep "Window id" | cut -d ' ' -f 4)
-  #XIMAGESRC_ARGS="xid=$XID"
+  WIN_INFO="$(xwininfo $XWININFO_OPTIONS)"
 
   X=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left X:[[:space:]]*/s///p")
   Y=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left Y:[[:space:]]*/s///p")
   WIDTH=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Width:[[:space:]]*/s///p")
   HEIGHT=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Height:[[:space:]]*/s///p")
 
 
   X=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left X:[[:space:]]*/s///p")
   Y=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left Y:[[:space:]]*/s///p")
   WIDTH=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Width:[[:space:]]*/s///p")
   HEIGHT=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Height:[[:space:]]*/s///p")
 
-  BORDER_SIZE=27
-  XIMAGESRC_ARGS="startx=$(($X - $BORDER_SIZE)) starty=$(($Y - $BORDER_SIZE)) endx=$(($X + $WIDTH + $BORDER_SIZE)) endy=$(($Y + $HEIGHT + $BORDER_SIZE))"
-
-  XIMAGESRC_ARGS="startx=$(($X - $BORDER_SIZE - 1)) starty=$(($Y - $BORDER_SIZE - 7)) endx=$(($X + $WIDTH + $BORDER_SIZE)) endy=$(($Y + $HEIGHT + $BORDER_SIZE + 7))"
+  XIMAGESRC_ARGS="startx=$(($X + $SHADOW_SIZE - $SHADOW_X_OFFSET)) starty=$(($Y + $SHADOW_SIZE - $SHADOW_Y_OFFSET)) endx=$(($X + $WIDTH - 1 - $SHADOW_SIZE - $SHADOW_X_OFFSET)) endy=$(($Y + $HEIGHT - 1 - $SHADOW_SIZE - $SHADOW_Y_OFFSET))"
 else
 else
-  XIMAGESRC_ARGS="$2"
+  XIMAGESRC_ARGS="$@"
 fi
 
 fi
 
-VIDEO_CODEC="video/x-raw,format=I420 ! jpegenc quality=90"
-#VIDEO_CODEC="openjpegenc"
+if [ $SINGLE_SHOT -eq 1 ];
+then
+  gst-launch-1.0 -v \
+    ximagesrc use-damage=0 show-pointer=$SHOW_POINTER $XIMAGESRC_ARGS num-buffers=1 ! \
+    videoconvert ! pngenc ! filesink location="$FILENAME"
+else
+  VIDEO_CODEC="video/x-raw,format=I420 ! jpegenc quality=90"
 
 
-gst-launch-1.0 -v \
-  ximagesrc use-damage=0 show-pointer=0 $XIMAGESRC_ARGS ! video/x-raw,framerate=25/1 ! \
-  videoconvert ! videorate ! $VIDEO_CODEC ! queue ! mux. \
-  matroskamux name=mux ! filesink location="$FILENAME"
-  #pulsesrc ! audioconvert ! 'audio/x-raw,rate=44100,channels=2' ! queue ! mux. \
+  if [ $CAPTURE_AUDIO -eq 1 ];
+  then
+    AUDIO_PIPELINE="pulsesrc ! audioconvert ! audio/x-raw,rate=44100,channels=2 ! queue ! mux."
+  fi
+
+  gst-launch-1.0 -v -e \
+    matroskamux name=mux ! filesink location="$FILENAME" \
+    ximagesrc use-damage=0 show-pointer=$SHOW_POINTER $XIMAGESRC_ARGS ! video/x-raw,framerate=25/1 ! \
+    videoconvert ! videorate ! $VIDEO_CODEC ! queue ! mux. \
+    $AUDIO_PIPELINE
+fi