this repo contains two analogous script prototypes for screencasting to livecoding.tv
- ffmpeg implementation - full-featured and works quite well
- gstreamer implementetion - work in progress
this repo contains two analogous script prototypes for screencasting to livecoding.tv
| #!/bin/bash | |
| THIS_NAME=`basename $0` | |
| USAGE="USAGE: | |
| $THIS_NAME [ a_configuration [ once ] ] | |
| when called with no arguments the stream runs the default configuration, automatically | |
| restarting after any exceptional event (device error , dropped connection , etc) | |
| the optional \"a_configuration\" selects a particular configuration and is one of: | |
| [ screen , screen+text , screen+cam , cam+text , all , break ] | |
| the optional second argument 'once' disables automatic restarts | |
| NOTE: STREAM_KEY must be defined either in this script or in the environment | |
| NOTE: all configurations include audio - set CAPTURE_AUDIO to 0 to disable" | |
| # the entries under the following headings are intended for per-user configuration: | |
| # * user a/v params (STREAM_KEY must be defined) | |
| # * user image params | |
| # * user text params | |
| # * user text configuration | |
| # * user a/v configuration | |
| # * user logging configuration | |
| # | |
| # the entries under the following headings are general and suitable for most cases: | |
| # * general a/v params | |
| # * general text params | |
| # * general logging params | |
| # * general a/v configuration | |
| # | |
| # when one of the configurations displaying text is chosen | |
| # the first line of $TEXT_FILE will be rendered | |
| # user a/v params | |
| [ ! -z "$STREAM_KEY" ] || STREAM_KEY="" # your private stream key (or define STREAM_KEY in env) | |
| SCREEN_W=1280 # input screen width (e.g. desktop width ) | |
| SCREEN_H=800 # input screen height (e.g. desktop height) | |
| SCREEN_N=":0.0" # display/screen number to capture (usually :0.0) | |
| CAM_LG_W=640 # input screen width (e.g. desktop width ) | |
| CAM_LG_H=480 # input screen height (e.g. desktop height) | |
| CAM_SM_W=160 # input screen width (e.g. desktop width ) | |
| CAM_SM_H=120 # input screen height (e.g. desktop height) | |
| CAM_DEV=/dev/video42 # video device to grab (first is /dev/video0) | |
| STREAM_W=1280 # input screen width (e.g. desktop width ) | |
| STREAM_H=800 # input screen height (e.g. desktop height) | |
| FPS=12 # target output FPS | |
| GOP="$(($FPS*4))" # i-frame interval | |
| CBR="1200k" # constant output bitrate (should be between 800k - 1300k) | |
| CAPTURE_AUDIO=1 # set to 1 to capture audio - 0 to disable audio | |
| AUDIO_CHANNELS=2 # number of channels | |
| AUDIO_SAMPLERATE=22050 # audio samplerate (22050 is plenty for voice) | |
| AUDIO_BITRATE="128k" # audio bitrate (64k per channel is plenty for voice) | |
| ALSA_DEVICE="hw:0" # ALSA plug device (usually hw:0) | |
| THREADS=0 # 0 for auto - max 6 | |
| QUALITY="ultrafast" # one of the many FFMPEG presets | |
| EXIT_CMD="play-error-sound.sh" | |
| # user image params | |
| [ ! -z "$STREAM_IMG" ] || STREAM_IMG="" # fullscreen interstitial image (or define STREAM_IMG in env) | |
| [ ! -z "$LOGO_IMG" ] || LOGO_IMG="" # small overlay image (or define LOGO_IMG in env) | |
| # user text params | |
| TEXT_FILE=/run/user/`id -u`/av-caster-text | |
| [ ! -z "$STREAM_TEXT" ] || STREAM_TEXT="" # initial overlay text (or define STREAM_TEXT in env) | |
| FONT_FILE=/usr/share/fonts/truetype/tlwg/Purisa.ttf | |
| FONT_SIZE=48 | |
| TEXT_BORDER_D=4 | |
| TEXT_SHADOW_W=8 | |
| TEXT_SHADOW_H=8 | |
| BLINK_TEXT_PERIOD=300 # repeat interval in seconds | |
| TEXT_MARQUEE_SPEED=6 # ~= n characters per second | |
| TEXT_COLOR='[email protected]' | |
| TEXT_BORDER_COLOR='[email protected]' | |
| TEXT_BG_COLOR='[email protected]' | |
| # general a/v params | |
| SCREEN_RES=$SCREEN_W'x'$SCREEN_H # screen capture input resolution | |
| CAM_HI_RES=$CAM_LG_W'x'$CAM_LG_H # webcam input/output high resolution (fullscreen) | |
| CAM_LO_RES=$CAM_SM_W'x'$CAM_SM_H # webcam input/output low resolution (overlay) | |
| STREAM_RES=$STREAM_W'x'$STREAM_H # mix output resolution | |
| SCREENCAP_INPUT="-f x11grab -s $SCREEN_RES -r $FPS -i $SCREEN_N" | |
| WEBCAM_INPUT_LG="-f video4linux2 -s $CAM_HI_RES -i $CAM_DEV" | |
| WEBCAM_INPUT_SM="-f video4linux2 -s $CAM_LO_RES -i $CAM_DEV" | |
| STATIC_INPUT_LG="-f image2 -loop 1 -r $FPS -i $STREAM_IMG" | |
| STATIC_INPUT_SM="-f image2 -loop 1 -r $FPS -i $LOGO_IMG" # unused | |
| ALSA_INPUT=" -f alsa -i $ALSA_DEVICE" # unused | |
| PULSE_INPUT=" -f pulse -name $THIS_NAME -sample_rate $AUDIO_SAMPLERATE | |
| -channels $AUDIO_CHANNELS -i default" # unused | |
| JACK_INPUT=" -f jack -channels $AUDIO_CHANNELS -i $THIS_NAME" | |
| OVERLAY_TL_POS="0: 0" # unused | |
| OVERLAY_BR_POS="main_w-overlay_w: main_h-overlay_h" | |
| X264_OUTPUT="-vcodec libx264 -pix_fmt yuv420p -preset $QUALITY -s $STREAM_RES | |
| -g $GOP -vb $CBR -maxrate $CBR -bufsize $CBR" | |
| AAC_OUTPUT="-acodec aac -ar $AUDIO_SAMPLERATE -ab $AUDIO_BITRATE -strict experimental" | |
| THREADS="-threads $THREADS" | |
| RTMP_URL="rtmp://usmedia3.livecoding.tv:1935/livecodingtv/$STREAM_KEY" | |
| DUMP_FILE=./livecoding.mp4 | |
| FILE_OUTPUT="-f flv $DUMP_FILE" | |
| LIVE_OUTPUT="-f flv $RTMP_URL" | |
| # general text params | |
| FONT='fontfile='$FONT_FILE': fontsize='$FONT_SIZE': fontcolor='$TEXT_COLOR': shadowx='$TEXT_SHADOW_W' : shadowy='$TEXT_SHADOW_H | |
| TEXT_BG='box=1: boxcolor='$TEXT_BG_COLOR # unused | |
| TEXT_BORDER='borderw='$TEXT_BORDER_D': bordercolor='$TEXT_BORDER_COLOR | |
| [ ! -z "$TEXT_FILE" ] && [ -f $TEXT_FILE ] && STREAM_TEXT=`cat $TEXT_FILE` | |
| BLINK_TEXT_DURATION=$((${#STREAM_TEXT}/$TEXT_MARQUEE_SPEED)) | |
| BLINK_TEXT=': enable=lt(mod(t\,'$BLINK_TEXT_PERIOD')\,'$BLINK_TEXT_DURATION')' # unused | |
| STATIC_TEXT='text='$STREAM_TEXT # unused | |
| TEXT_X_CENTER_POS='x=(main_w-text_w)/2' | |
| TEXT_Y_TOP_POS='y=0' # unused | |
| TEXT_Y_BOTTOM_POS='y=main_h-(line_h*1.5)' | |
| TEXT_X_MARQUEE_POS='x=main_w-((('$SCREEN_W'+text_w)/'$BLINK_TEXT_DURATION')*mod(t\,'$BLINK_TEXT_DURATION'))'$BLINK_TEXT | |
| FILE_TEXT="textfile=$TEXT_FILE: reload=1" | |
| # general logging params | |
| LOGGING_NONE="-loglevel quiet" | |
| LOGGING_ERRORS="-loglevel error" | |
| LOGGING_VERBOSE="" | |
| # user a/v configuration | |
| OVERLAY_INPUT=$WEBCAM_INPUT_SM # one of WEBCAM_INPUT_SM , STATIC_INPUT_SM | |
| OVERLAY_POS=$OVERLAY_BR_POS # one of OVERLAY_TL_POS , OVERLAY_BR_POS | |
| AUDIO_INPUT=$JACK_INPUT # one of ALSA_INPUT , PULSE_INPUT , JACK_INPUT | |
| VIDEO_OUTPUT=$X264_OUTPUT # only X264_OUTPUT is defined (others are possible) | |
| AUDIO_OUTPUT=$AAC_OUTPUT # only AAC_OUTPUT is defined (others are possible) | |
| STREAM_OUTPUT=$LIVE_OUTPUT # one of FILE_OUTPUT , LIVE_OUTPUT | |
| # general a/v configuration | |
| [ ! -z "$TEXT_FILE" ] && [ ! -f $TEXT_FILE ] && echo "$STREAM_TEXT" > $TEXT_FILE | |
| (($CAPTURE_AUDIO)) || (AUDIO_INPUT="" ; AUDIO_OUTPUT="") | |
| # user text configuration | |
| TEXT=": $FILE_TEXT" | |
| # TEXT_POS=": $TEXT_X_CENTER_POS: $TEXT_Y_BOTTOM_POS" | |
| TEXT_POS=": $TEXT_X_MARQUEE_POS: $TEXT_Y_BOTTOM_POS" | |
| TEXT_DECORATIONS=": $TEXT_BORDER" | |
| DRAWTEXT_PARAMS="$FONT $TEXT $TEXT_POS $TEXT_DECORATIONS" | |
| # user logging configuration | |
| LOGGING=$LOGGING_NONE # console output (one of LOGGING_NONE , LOGGING_ERRORS , LOGGING_VERBOSE) | |
| screenOnly() | |
| { | |
| printf "\nlaunching screenOnly configuration\n\n" | |
| ffmpeg $SCREENCAP_INPUT \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| echo "QUIT=$QUIT" | |
| } | |
| screenPlusText() | |
| { | |
| printf "\nlaunching screenPlusText configuration\n\n" | |
| ffmpeg $SCREENCAP_INPUT \ | |
| -filter_complex drawtext="$DRAWTEXT_PARAMS" \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| } | |
| screenPlusCam() | |
| { | |
| printf "\nlaunching screenPlusCam configuration\n\n" | |
| avconv $SCREENCAP_INPUT \ | |
| $OVERLAY_INPUT \ | |
| -filter_complex overlay="$OVERLAY_POS" \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| } | |
| camPlusText() | |
| { | |
| printf "\nlaunching camPlusText configuration\n\n" | |
| ffmpeg $WEBCAM_INPUT_LG \ | |
| -filter_complex drawtext="$DRAWTEXT_PARAMS" \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| } | |
| screenPlusCamPlusText() | |
| { | |
| printf "\nlaunching screenPlusCamPlusText configuration\n\n" | |
| ffmpeg $SCREENCAP_INPUT \ | |
| $OVERLAY_INPUT \ | |
| -filter_complex "overlay='$OVERLAY_POS', drawtext='$DRAWTEXT_PARAMS'" \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| } | |
| interstitial() | |
| { | |
| printf "\nlaunching interstitial configuration\n\n" | |
| ffmpeg $STATIC_INPUT_LG \ | |
| $AUDIO_INPUT \ | |
| $VIDEO_OUTPUT \ | |
| $AUDIO_OUTPUT \ | |
| $THREADS \ | |
| $LOGGING \ | |
| $STREAM_OUTPUT | |
| QUIT=$? | |
| } | |
| launch() | |
| { | |
| printf "\nstream started at `date`\n\n$QUIT_MSG" ; | |
| $SELECTED_CONFIGURATION | |
| printf "\nstream ended at `date`\n\n" ; | |
| } | |
| # swicth on configuration | |
| SELECTED_CONFIGURATION="screenPlusCamPlusText" | |
| [ "$1" == "screen" ] && SELECTED_CONFIGURATION="screenOnly" | |
| [ "$1" == "screen+text" ] && SELECTED_CONFIGURATION="screenPlusText" | |
| [ "$1" == "screen+cam" ] && SELECTED_CONFIGURATION="screenPlusCam" | |
| [ "$1" == "cam+text" ] && SELECTED_CONFIGURATION="camPlusText" | |
| [ "$1" == "all" ] && SELECTED_CONFIGURATION="screenPlusCamPlusText" | |
| [ "$1" == "break" ] && SELECTED_CONFIGURATION="interstitial" | |
| # sanity checks | |
| if [ -z "$STREAM_KEY" ] ; then echo "STREAM_KEY undefined" ; exit ; fi ; | |
| if [ -z "$STREAM_IMG" ] && [ "$SELECTED_CONFIGURATION" == "interstitial" ] | |
| then echo "STREAM_IMG undefined" ; exit ; fi ; | |
| # launch configuration once | |
| QUIT_MSG="press <q> to exit" | |
| if [ "$2" == "once" ] ; then launch ; exit ; fi ; | |
| # launch configuration indefinitely | |
| QUIT_MSG="press <q> to restart the stream - press <CTRL-c> to exit" | |
| # while ! (($QUIT)) ; do sleep 1 ; launch ; $EXIT_CMD ; done ; | |
| # TODO: ffmpeg exits with 0 on server kick - need some way to trap this | |
| while ((1)) ; do sleep 1 ; launch ; $EXIT_CMD ; done ; |
| #!/bin/bash | |
| # NOTE: this script is not yet working properly | |
| # screencap works well as do videotestsrc and audiotestsrc | |
| # but using alsa , pulse , or jack audio cause the audio to be intermittent | |
| # e.g. recording "test 1 2 3 4 5 6 7 8 9" results like "test 3 6 8 9" | |
| # configuration | |
| WIDTH=1280 | |
| HEIGHT=800 | |
| FPS=12 | |
| VIDEO_BITRATE=1200 | |
| SAMPLERATE=44100 | |
| AUDIO_BITRATE=128 | |
| N_CHANNELS=2 | |
| # constants | |
| VIDEO_TEST_SRC='videotestsrc pattern=0 is-live=true' | |
| AUDIO_TEST_SRC='audiotestsrc is-live=true' | |
| # SCREENCAP_SRC="ximagesrc endx=$(($WIDTH-1)) endy=$(($HEIGHT-1)) use-damage=false show-pointer=true" | |
| # TIME_SRC='timeoverlay halignment=left valignment=top text="t:" shaded-background=true' | |
| PULSE_SRC='pulsesrc' | |
| H264_ENCODER="x264enc bitrate=$VIDEO_BITRATE bframes=0" | |
| LAME_ENCODER="lamemp3enc bitrate=$AUDIO_BITRATE" | |
| # VIDEO_TEST_CAPS="video/x-raw, format=(string)BGRA, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=$FPS/1, width=$WIDTH, height=$HEIGHT" | |
| # AUDIO_TEST_CAPS="audio/x-raw, format=(string)S16LE, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)$SAMPLERATE, channels=(int)$N_CHANNELS" | |
| # PULSE_CAPS=" audio/x-raw, format=(string)S16LE, rate=(int)$SAMPLERATE, channels=(int)$N_CHANNELS, layout=(string)interleaved" | |
| VIDEO_TEST_CAPS="video/x-raw, format=(string)BGRA, framerate=(fraction)$FPS/1, width=(int)$WIDTH, height=(int)$HEIGHT" | |
| AUDIO_TEST_CAPS="audio/x-raw, format=(string)S16LE, rate=(int)$SAMPLERATE, channels=(int)$N_CHANNELS" | |
| SCREENCAP_CAPS=" video/x-raw, framerate=(fraction)$FPS/1, width=(int)$WIDTH, height=(int)$HEIGHT, pixel-aspect-ratio=(fraction)1/1" | |
| PULSE_CAPS=" audio/x-raw, format=(string)S16LE, rate=(int)$SAMPLERATE, channels=(int)$N_CHANNELS" | |
| H264_ENC_CAPS=' video/x-h264, level=(string)4.1, profile=main' | |
| FLV_MUXER='flvmux streamable=true name=mux' | |
| OUTPUT_FILE=./deleteme.flv | |
| FILE_OUTPUT="filesink location=$OUTPUT_FILE" | |
| DEBUG_LOGGING='--gst-debug-level=2' | |
| VERBOSE_LOGGING='--verbose' | |
| # input selections | |
| VIDEO_SRC=$VIDEO_TEST_SRC | |
| VIDEO_CAPS=$VIDEO_TEST_CAPS | |
| # OVERLAY_SRC=$TIME_SRC # NOTE: put OVERLAY_SRC between VIDEO_SRC and VIDEO_CAPS | |
| VIDEO_ENC_CAPS=$H264_ENC_CAPS | |
| VIDEO_ENCODER=$H264_ENCODER | |
| # AUDIO_SRC=$AUDIO_TEST_SRC | |
| AUDIO_SRC=$PULSE_SRC | |
| # AUDIO_CAPS=$AUDIO_TEST_CAPS | |
| AUDIO_CAPS=$PULSE_CAPS | |
| AUDIO_ENCODER=$LAME_ENCODER | |
| MUXER=$FLV_MUXER | |
| OUTPUT_STREAM=$FILE_OUTPUT | |
| LOGGING="$DEBUG_LOGGING $VERBOSE_LOGGING" | |
| gst-launch-1.0 $LOGGING $VIDEO_SRC ! \ | |
| $VIDEO_CAPS ! \ | |
| queue ! \ | |
| videoconvert ! \ | |
| $VIDEO_ENCODER ! \ | |
| h264parse ! \ | |
| $VIDEO_ENC_CAPS ! \ | |
| queue ! \ | |
| mux. \ | |
| $AUDIO_SRC ! \ | |
| $AUDIO_CAPS ! \ | |
| queue ! \ | |
| queue ! \ | |
| $AUDIO_ENCODER ! \ | |
| mpegaudioparse ! \ | |
| queue ! \ | |
| queue ! \ | |
| queue ! \ | |
| mux. \ | |
| $MUXER ! \ | |
| queue ! \ | |
| $OUTPUT_STREAM |