diff --git a/README.md b/README.md index e384625..514f9fd 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,18 @@ $ giph -g 100x200+0+0 -d 5 -t 10 ``` Records a 100x200 pixel rectangle in the top left corner of the screen. The recording starts after a 5 seconds countdown and will record for exactly 10 seconds. The resulting gif will be printed to standard output, which makes this able to be piped into other scripts like a file-upload to an image hosting service. + +```bash +$ giph -f 30 -t 5 -s -a -m out.webm +``` +Records a 5 second video of the users selection at 30 fps. The recording also contains the users desktop audio and microphone. If the recording fails because the default audio source `0` is not the correct one, run `pacmd list-sources` to get the correct source `index` or `name` and pass it to the `-as` parameter instead of using `-a`. Example: `giph -f 30 -t 5 -s -as 1 -m out.webm` (using id) or `giph -f 30 -t 5 -s -as alsa_output.pci-0000_04_00.1.hdmi-stereo.monitor -m out.webm` (using name) + +```bash +$ giph -s -t 10 --format webm | curl -F "file=@-" 0x0.st | xclip -selection clipboard +``` + +Records a 10 second webm of the users selection, uploads the video to 0x0.st using curl and copies the returned url to the clipboard. + ## Installation ### Arch diff --git a/man/giph.1 b/man/giph.1 index 1686b27..4b2336e 100644 --- a/man/giph.1 +++ b/man/giph.1 @@ -12,10 +12,17 @@ giph \- record gif from desktop, window or selection [\fB-d\fR \fIINT\fR] [\fB-t\fR \fIINT\fR] [\fB-f\fR \fIINT\fR] +[\fB--format\fR \fISTRING\fR] +[\fB-a\fR | \fB -as \fISTRING\fR] +[\fB-m\fR | \fB -ms \fISTRING\fR] [\fIFILENAME\fR] .SH DESCRIPTION .B giph -is a screen recorder that records the desktop, a window or selection and encodes it into a gif file. It prints the encoded gif directly to standard output when omitting [\fIFILENAME\fR]. +is a screen recorder that records the desktop, a window or selection and encodes it into a video file. +.br +The recorded video file is encoded in one of the supported formats, gif, webm, mp4 or mkv based on the file extension given in [\fIFILENAME\fR]. +.br +When omitting [\fIFILENAME\fR], the default format "gif" is used (use --format to overwrite), and the resulting video directly printed to standard output. .SH EXAMPLES .TP .BI "giph -g " "300x200+600+200 ~/Videos/$(date +%s).png" @@ -71,9 +78,32 @@ Sets the time in seconds to wait before the recording starts. .BR \-t ", " \-\-timer " " \fITIMEDURATION Sets a fixed time to record. The format is a timeduration as described in the ffmpeg documentation (https://ffmpeg.org/ffmpeg-utils.html#Time-duration). As an example, '10' would mean 10 seconds, '3:30' means 3 minutes and 30 seconds, '1:02:03' means 1 hour, 2 minutes and 3 seconds, and '5.5' means 5.5 seconds. .TP -.BR \-f ", " \-\-framerate " \fIINT\fR (default: \fI15\fR)" +.BR \-f ", " \-\-framerate " \fIINT\fR (default: \fI20\fR)" Sets the desired framerate of the recorded gif. A higher framerate will result in a larger filesize. .TP +.BR \-\-format +Overwrites the format that should be used for the recording. The available formats are +.IR gif ", " webm ", " mp4 " and " mkv "." +While +.IR gif +is the only format that does not support audio recording. +.TP +.BR \-a ", " \-\-audio +Enables audio recording for formats that support audio. +.TP +.BR \-as ", " \-\-audio-source " \fISTRING\fR (default: '\fI0\fR')" +Define which pulseaudio source should be used for the recording. Run `pacmd list-sources` to get a list of all available sources. You can use either the index or the name of the source for this parameter. When setting this parameter, setting +.BR \-a " or " \-\-audio +can be omitted. +.TP +.BR \-m ", " \-\-microphone +Enables microphone recording for formats that support audio. +.TP +.BR \-ms ", " \-\-microphone-source " \fISTRING\fR (default: '\fIdefault\fR')" +Define which pulseaudio source should be used for the recording. Run `pacmd list-sources` to get a list of all available sources. You can use either the index or the name of the source for this parameter. When setting this parameter, setting +.BR \-m " or " \-\-microphone +can be omitted. +.TP .BR \-y ", " \-\-notify Uses notify-send to send an urgent notification if an error happens, or a normal notification when the final gif was saved successfully. .SH SLOP OPTIONS diff --git a/src/giph b/src/giph index 22beee7..c3d94aa 100755 --- a/src/giph +++ b/src/giph @@ -10,7 +10,12 @@ VERBOSITY=0 # options SLOP=0 DELAY=0 -FRAMERATE=15 +FRAMERATE=20 +AUDIO=0 +AUDIO_SOURCE=0 +MICROPHONE=0 +MICROPHONE_SOURCE=default +FORMAT_OVERWRITE="" function print_version() { echo $VERSION @@ -23,21 +28,27 @@ SYNOPSIS giph [OPTIONS] [FILENAME] DESCRIPTION - giph is a screen recorder that records the desktop, a window or selection and encodes it into a gif file. - It prints the encoded gif directly to standard output when omitting [FILENAME]. + giph is a screen recorder that records the desktop, a window or selection and encodes it into a video file. + The recorded video file is encoded in one of the supported formats, gif, webm, mp4 or mkv based on the file extension given in [FILENAME]. + When omitting [FILENAME], the default format "gif" is uded (use --format to overwrite), and the resulting video directly printed to standard output. OPTIONS - -h, --help Print help and exit. - --version Print version and exit. - -v*, --verbose, --quiet Set the verbosity. - -s, --select Enable slop selection. - -g, --geometry=STRING Record rectangle by geometry. (like 100x300+0+0) - -w, --window=INT Record window by id. - -d, --delay=INT Time in seconds before the recording starts. - -t, --timer=TIMEDURATION Time of the recording. (e.g. 10 for 10 seconds or 1:30 for 1 minute 30 seconds) - -f, --framerate=INT Set the framerate. - -y, --notify Send notification on error or success. - --stop Finish any running giph recordings. + -h, --help Print help and exit. + --version Print version and exit. + -v*, --verbose, --quiet Set the verbosity. + -s, --select Enable slop selection. + -g, --geometry=STRING Record rectangle by geometry. (like 100x300+0+0) + -w, --window=INT Record window by id. + -d, --delay=INT Time in seconds before the recording starts. + -t, --timer=TIMEDURATION Time of the recording. (e.g. 10 for 10 seconds or 1:30 for 1 minute 30 seconds) + -f, --framerate=INT Set the framerate. + --format Set the wanted output format. This overwrites the autodetection. + -a, --audio Enable audio recording. + -as, --audio-source=STRING Overwrite the default audio source. + -m, --microphone Enable microphone recording. + -ms, --microphone-source=STRING Overwrite the default microphone source. + -y, --notify Send notification on error or success. + --stop Finish any running giph recordings. SLOP OPTIONS -b, --bordersize=FLOAT Set the selection border thickness. @@ -142,6 +153,26 @@ while [[ "$1" == -* ]]; do shift FRAMERATE="$1" ;; + --format) + shift + FORMAT_OVERWRITE="$1" + ;; + -a|--audio) + AUDIO=1 + ;; + -as|--audio-source) + shift + AUDIO=1 + AUDIO_SOURCE="$1" + ;; + -m|--microphone) + MICROPHONE=1 + ;; + -ms|--microphone-source) + shift + MICROPHONE=1 + MICROPHONE_SOURCE="$1" + ;; -y|--notify) NOTIFY=1 ;; @@ -188,6 +219,20 @@ done # set verbosity to -1 if the file should be printed to stdout [ -n "$1" ] && OUTPUT_FILE=$1 || VERBOSITY=-1 +case "$OUTPUT_FILE" in + *.webm) FORMAT="webm" ;; + *.mp4) FORMAT="mp4" ;; + *.mkv) FORMAT="mkv" ;; + *) FORMAT="gif" ;; +esac + +if [ -n "$FORMAT_OVERWRITE" ]; then + case "$FORMAT_OVERWRITE" in + webm|mp4|mkv|gif) FORMAT="$FORMAT_OVERWRITE";; + *) log_error "'$FORMAT_OVERWRITE' is not a supported format." + esac +fi + function get_geometry() { if [ $SLOP = 1 ]; then log "using slop to determine recording geometry" 1 true @@ -299,25 +344,40 @@ function create_temporary_directory() { log "created temporary directory $TEMP_DIRECTORY" 2 true } -function start_video_recording() { - ffmpeg=(ffmpeg) - ffmpeg+=(-f x11grab) - ffmpeg+=(-framerate "$FRAMERATE") - ffmpeg+=(-s "$width"x"$height") - ffmpeg+=(-i ":0.0+$x,$y") - ffmpeg+=(-preset ultrafast) - ffmpeg+=(-crf 0) +function record() { + ffmpeg=(ffmpeg -f x11grab -s "$width"x"$height" -i ":0.0+$x,$y") + + if [ "$FORMAT" != "gif" ]; then + [ "$AUDIO" = 1 ] && ffmpeg+=(-f pulse -i "$AUDIO_SOURCE") + [ "$MICROPHONE" = 1 ] && ffmpeg+=(-f pulse -i "$MICROPHONE_SOURCE") + [ "$AUDIO" = 1 ] && [ "$MICROPHONE" = 1 ] && ffmpeg+=(-filter_complex amerge -ac 2) + fi + + ffmpeg+=(-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2") [ "$VERBOSITY" -lt "3" ] && ffmpeg+=(-loglevel "quiet") [ -n "$TIMER" ] && [ "$TIMER" -gt 0 ] && ffmpeg+=(-t "$TIMER") - ffmpeg+=("$TEMP_DIRECTORY/recording.mkv") + ffmpeg+=(-r $FRAMERATE) + + case "$FORMAT" in + mp4|mkv) + ffmpeg+=(-pix_fmt yuv420p) + ffmpeg+=(-crf 15) + ffmpeg+=("$TEMP_DIRECTORY/final.$FORMAT") + ;; + gif|webm|*) + ffmpeg+=(-preset veryslow) + ffmpeg+=(-crf 0) + ffmpeg+=("$TEMP_DIRECTORY/lossless.mp4") + ;; + esac [ -n "$DELAY" ] && [ "$DELAY" -gt 0 ] && countdown_cli "$DELAY" "recording starts in" ffmpeg_command=${ffmpeg[*]} log "ffmpeg command: '$ffmpeg_command'" 2 true - + "${ffmpeg[@]}" & FFMPEG_PID=$! @@ -348,37 +408,43 @@ function countdown_cli() { done } - function stop_recording_handler_cli() { log_info "stop recording with \033[1;36mctrl+c\033[0m or call \033[1;36mgiph --stop\033[0m" trap '' INT } +function encode() { + log "encoding $FORMAT using ffmpeg" 1 true -function encode_gif_ffmpeg() { - log "encoding gif using ffmpeg encoder" 1 true - ffmpeg_generate_palette=(ffmpeg -i "$TEMP_DIRECTORY/recording.mkv" -vf palettegen "$TEMP_DIRECTORY/palette.png") - [ "$VERBOSITY" -lt "3" ] && ffmpeg_generate_palette+=(-loglevel "quiet") - - log "generating color palette from recording" 2 true - "${ffmpeg_generate_palette[@]}" - [ $? = 1 ] && log_error "could not generate color palette from video" - - ffmpeg_encode_gif=(ffmpeg -i "$TEMP_DIRECTORY/recording.mkv" -i "$TEMP_DIRECTORY/palette.png" -filter_complex paletteuse -r "$FRAMERATE" "$TEMP_DIRECTORY/encoded.gif") - [ "$VERBOSITY" -lt "3" ] && ffmpeg_encode_gif+=(-loglevel "quiet") - - log "encoding gif using recorded video and generated palette" 2 true - "${ffmpeg_encode_gif[@]}" - [ $? = 1 ] && log_error "could not encode gif from video and color palette" + ffmpeg_encode=(ffmpeg -i "$TEMP_DIRECTORY/lossless.mp4") + + case "$FORMAT" in + "gif") + ffmpeg_encode+=(-vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse") + ;; + "webm") + ffmpeg_encode+=(-c:v libvpx-vp9) + ;; + esac + + [ "$VERBOSITY" -lt "3" ] && ffmpeg_encode+=(-loglevel "quiet") + + ffmpeg_encode+=("$TEMP_DIRECTORY/final.$FORMAT") + + ffmpeg_encode_command=${ffmpeg_encode[*]} + log "ffmpeg encode command: '$ffmpeg_encode_command'" 2 true + + "${ffmpeg_encode[@]}" + [ $? = 1 ] && log_error "could not encode $FORMAT from lossless recording" } -function deliver_final_gif() { +function deliver() { if [ -n "$OUTPUT_FILE" ]; then - mv "$TEMP_DIRECTORY/encoded.gif" "$OUTPUT_FILE" && { - log_success "final gif saved as \"$OUTPUT_FILE\"" + mv "$1" "$OUTPUT_FILE" && { + log_success "final $FORMAT saved as \"$OUTPUT_FILE\"" } else - cat "$TEMP_DIRECTORY/encoded.gif" + cat "$1" fi } @@ -391,9 +457,14 @@ function delete_temporary_directory() { function giph() { get_geometry create_temporary_directory - start_video_recording - encode_gif_ffmpeg - deliver_final_gif + record + + if [[ "$FORMAT" == "gif" || "$FORMAT" == "webm" ]]; then + encode + fi + + deliver "$TEMP_DIRECTORY/final.$FORMAT" + delete_temporary_directory exit 0 }