├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── ffscreencast ├── composer.json ├── config └── ffscreencastrc ├── contrib ├── README.md ├── ffc-to-gif └── ffc-to-mp4 └── doc └── img ├── ffscreencast.png └── ffscreencast2.png /.gitignore: -------------------------------------------------------------------------------- 1 | ###################################### 2 | # CUSTOM 3 | ###################################### 4 | 5 | build/* 6 | !build/.keepme 7 | 8 | 9 | ###################################### 10 | # GENERIC 11 | ###################################### 12 | 13 | ###### std ###### 14 | .lock 15 | *.log 16 | 17 | ###### patches/diffs ###### 18 | *.patch 19 | *.diff 20 | *.orig 21 | *.rej 22 | 23 | 24 | ###################################### 25 | # Operating Systems 26 | ###################################### 27 | 28 | ###### OSX ###### 29 | ._* 30 | .DS* 31 | .Spotlight-V100 32 | .Trashes 33 | 34 | ###### Windows ###### 35 | Thumbs.db 36 | ehthumbs.db 37 | Desktop.ini 38 | $RECYCLE.BIN/ 39 | *.lnk 40 | 41 | 42 | ###################################### 43 | # Editors 44 | ###################################### 45 | 46 | ###### Sublime ###### 47 | *.sublime-workspace 48 | *.sublime-project 49 | 50 | ###### Eclipse ###### 51 | .classpath 52 | .buildpath 53 | .project 54 | .settings/ 55 | 56 | ###### Netbeans ###### 57 | nbproject/private/ 58 | 59 | ###### Intellij IDE ###### 60 | .idea/ 61 | .idea_modules/ 62 | 63 | ###### vim ###### 64 | *.swp 65 | *.swo 66 | *~ 67 | 68 | ###### TextMate ###### 69 | .tm_properties 70 | *.tmproj 71 | 72 | ###### BBEdit ###### 73 | *.bbprojectd 74 | *.bbproject 75 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | before_script: 4 | - sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ trusty-backports restricted main universe" 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq shellcheck 7 | 8 | script: 9 | - shellcheck --exclude=SC2129 --shell=bash bin/ffscreencast 10 | - shellcheck --shell=bash contrib/ffc-to-gif 11 | - shellcheck --shell=bash contrib/ffc-to-mp4 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | Version 0.7 (unreleased) 5 | ----------- 6 | 7 | 8 | Version 0.6.4 9 | ------------- 10 | 11 | - [Fix] Linux: x11grab check for newer and older versions 12 | 13 | -Version 0.6.3 14 | ------------- 15 | 16 | - [Enh] Video to gif converter (see [contrib](contrib/)) 17 | - [Enh] Video to mp4 converter (see [contrib](contrib/)) 18 | 19 | 20 | Version 0.6.2 21 | ------------- 22 | 23 | - [Fix] Fix wording for listing rec devices 24 | 25 | Version 0.6.1 26 | ------------- 27 | 28 | - [Enh] Added config file in ~/.config/ffscreencast/ffscreencastrc 29 | - [Enh] Faster bootstrap with cache variables 30 | 31 | Version 0.6 32 | ----------- 33 | 34 | - [Enh] Major code rewrite to better support new methods/os 35 | - [Fix] Only check for requirements that are being requested 36 | - [Enh] Detailed info about missing requirements 37 | 38 | Version 0.5 39 | ----------- 40 | 41 | - [Fix] Fixed quoting issue 42 | - [Enh] Pass shellcheck 43 | - [Fix] Remove `-video_size` option if no resolution was found 44 | - [Fix] Replaced commands (grep, awk, sed) with full system path to avoid aliases 45 | 46 | Version 0.4 (beta) 47 | ------------------ 48 | 49 | - [Fix] OSX: Get default camera resolution/framerate for recording 50 | - [Enh] Code comments 51 | - [Enh] OSX: List camera resolutions and framerates 52 | - [Fix] Audio delay (without camera overlay) 53 | - [Fix] Fixed broken sound (now only delays about 1 second with cam overlay) 54 | 55 | Version 0.3 (beta) 56 | ------------------ 57 | 58 | - [Enh] Code cleanup 59 | - [Enh] OSX: Better screen (monitor) information 60 | - [Enh] Being able to change custom ffmpeg options via cmd argument 61 | 62 | Version 0.2 (beta) 63 | ------------------ 64 | 65 | - Added basic linux support 66 | - Added version information 67 | - Added check requirements 68 | - [Enh] Be able to list the ffmpeg command only 69 | - [Enh] Performance improvements on wrong commands 70 | 71 | Version 0.1 (alpha) 72 | ------------------- 73 | 74 | - screencast with video overlay 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Cytopia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ffscreencast 2 | 3 | [Features](https://github.com/cytopia/ffscreencast#1-features) | 4 | [Installation](https://github.com/cytopia/ffscreencast#2-installation) | 5 | [Usage](https://github.com/cytopia/ffscreencast#3-usage) | 6 | [Screenshots](https://github.com/cytopia/ffscreencast#4-screenshots) | 7 | [FAQ](https://github.com/cytopia/ffscreencast#5-faq) | 8 | [Todo](https://github.com/cytopia/ffscreencast#6-todo) | 9 | [Contribution](https://github.com/cytopia/ffscreencast#7-contribution) | 10 | [License](https://github.com/cytopia/ffscreencast#8-license) | 11 | [Version](https://github.com/cytopia/ffscreencast#9-version) | 12 | [Awesome](https://github.com/cytopia/ffscreencast#10-awesome) 13 | 14 | [![Build Status](https://travis-ci.org/cytopia/ffscreencast.svg?branch=master)](https://travis-ci.org/cytopia/ffscreencast) 15 | [![Latest Stable Version](https://poser.pugx.org/cytopia/ffscreencast/v/stable)](https://packagist.org/packages/cytopia/ffscreencast) [![Total Downloads](https://poser.pugx.org/cytopia/ffscreencast/downloads)](https://packagist.org/packages/cytopia/ffscreencast) [![Latest Unstable Version](https://poser.pugx.org/cytopia/ffscreencast/v/unstable)](https://packagist.org/packages/cytopia/ffscreencast) [![License](https://poser.pugx.org/cytopia/ffscreencast/license)](http://opensource.org/licenses/MIT) 16 | [![Type](https://img.shields.io/badge/type-bash-red.svg)](https://www.gnu.org/software/bash/) 17 | 18 | **About** 19 | 20 | `ffscreencast` is a shell wrapper for `ffmpeg` that allows fool-proof screen recording via the command line. It will auto-detect all available monitors, cameras and microphones and is able to interactively or manually choose the desired recording device(s). Additionally `ffscreencast` will let you overlay the camera stream on top of the desktop session. 21 | 22 | Besides that `ffscreencast` can act as an ffmpeg command generator. Every available option can also just show the corresponding ffmpeg command instead of executing it. Non-ffmpeg commands, such as how the camera resolution is pulled and others can also be shown instead of being executed. 23 | 24 | ![Screencast](https://raw.githubusercontent.com/cytopia/ffscreencast/master/doc/img/ffscreencast.png) 25 | 26 | **Supported platforms** 27 | 28 | | OSX | Linux | FreeBSD | Windows | 29 | | :----: | :----: | :----: | :----: | 30 | | [![OSX](https://raw.githubusercontent.com/cytopia/icons/master/64x64/osx.png)](https://www.apple.com/osx) | ![Linux](https://raw.githubusercontent.com/cytopia/icons/master/64x64/linux.png) | [![FreeBSD](https://raw.githubusercontent.com/cytopia/icons/master/64x64/freebsd.png)](https://www.freebsd.org) | [![Windows](https://raw.githubusercontent.com/cytopia/icons/master/64x64/windows.png)](https://www.microsoft.com/en-us/windows) | 31 | | via [AVFoundation](https://ffmpeg.org/ffmpeg-devices.html#avfoundation) | via [x11grab](https://ffmpeg.org/ffmpeg-devices.html#x11grab) | coming soon | coming soon | 32 | 33 | **Requirements** 34 | 35 | | Program | Required | Description | 36 | | ------------- | ------------- | -------- | 37 | | [bash](https://www.gnu.org/software/bash/) | yes | The whole script is written in bash and might not be 100% Posix compliant | 38 | | [ffmpeg](https://www.ffmpeg.org/) | yes | The ffmpeg binary must be present | 39 | | [v4l2-ctl](http://linuxtv.org/wiki/index.php/V4l-utils) | Linux | Required for linux to list camera devices | 40 | | [arecord](http://linux.die.net/man/1/arecord) | Linux | Required for linux to list sound devices | 41 | | [xdpyinfo](http://www.x.org/archive/X11R7.6/doc/man/man1/xdpyinfo.1.xhtml) | Linux | Required for linux to list screends | 42 | 43 | ## 1. Features 44 | 45 | * Config file for default configuration 46 | * Screen recording 47 | * Camera overlay 48 | * Audio support 49 | * Allows to manually (parameter) or interactively choose monitor 50 | * Allows to manually (parameter) or interactively choose camera 51 | * Allows to manually (parameter) or interactively choose sound device 52 | * ffmpeg command generation 53 | 54 | ## 2. Installation 55 | 56 | ### 2.1 OSX 57 | 58 | ```shell 59 | brew install cytopia/tap/ffscreencast 60 | ``` 61 | 62 | ### 2.2 Linux/BSD 63 | ```shell 64 | git clone https://github.com/cytopia/ffscreencast 65 | cd ffscreencast 66 | sudo cp bin/ffscreencast /usr/local/bin 67 | ``` 68 | 69 | ## 3. Usage 70 | 71 | ### 3.1 Overview 72 | 73 | To simply start desktop recording your screen call the program without any arguments `ffscreencast` and it will use the default screen without camera overlay and without sound. 74 | 75 | ```shell 76 | $ ffscreencast 77 | 78 | Usage: ffscreencast [-s[num]] [--sargs=] [-a[num]] [--aargs=] [-c[num] [--cargs=] [--oargs=] [-e] [--dry] 79 | ffscreencast --slist [--dry] 80 | ffscreencast --alist [--dry] 81 | ffscreencast --clist [--dry] 82 | ffscreencast --help 83 | ffscreencast --version 84 | ffscreencast --test 85 | 86 | When invoked without any arguments, it will start screen recording 87 | on the default screen without sound and without camera overlay. 88 | 89 | Input options: 90 | -s[num] (Default) Enable screen capturing [with device number X]. 91 | If no device number is specified it will use the default, if only 92 | one device is present, otherwise it will ask you to choose one 93 | Use: -s or -s1 94 | 95 | --sargs= Additional screen arguments. 96 | Specify additional ffmpeg arguments for the screen input device. 97 | Use: --sargs="-framerate 30" 98 | Default: '' 99 | 100 | -a[num] Enable audio capturing [with device number X] 101 | If no device number is specified it will use the default, if only 102 | one device is present, otherwise it will ask you to choose one 103 | Use: -a or -a1 104 | 105 | --aargs= Additional audio arguments. 106 | Specify additional ffmpeg arguments for the audio input device. 107 | Use: --aargs="-ac 1" 108 | Default: '-ac 2' 109 | 110 | -c[num] Add camera overlay [with device number X] 111 | If no device number is specified it will use the default, if only 112 | one device is present, otherwise it will ask you to choose one 113 | Use: -c or -c1 114 | 115 | --cargs= Additional camera arguments 116 | Specify additional ffmpeg arguments for the camera input device. 117 | Use: --cargs="-video_size 1280x720" 118 | Default: '' 119 | 120 | 121 | Output options: 122 | -e Output video format extension (Default: mkv) 123 | E.g.: -emkv, or -eavi, or -emp4 124 | 125 | -oargs= Additional output arguments 126 | Specify additional ffmpeg arguments for the output encoding. 127 | Use: --oargs="-crf 0" 128 | Default: '-crf 0 -preset ultrafast' 129 | 130 | 131 | Behavior options: 132 | --dry Show the command (without executing) 133 | 134 | 135 | List options: 136 | --list List all devices 137 | --slist Only list screen capturing devices (monitors) 138 | --alist Only list audio capturing devices (microphones) 139 | --clist Only list camera capturing devices (cams) 140 | 141 | 142 | System information: 143 | --help Show this help screen 144 | --version Show version information 145 | --test Test requirements 146 | 147 | ``` 148 | 149 | The `num` (device numbers) can be omitted. If there is only one device of its type available, `ffscreencast` will automatically default to this device, otherwise it will ask interactively which device to use for recording. 150 | 151 | ### 3.2 Examples 152 | 153 | Do a screencast on the default screen (without explicitly choosing the monitor) 154 | 155 | ```shell 156 | $ ffscreencast 157 | ``` 158 | 159 | List monitors and record on monitor 2 (`Capture screen 0`) 160 | 161 | ```shell 162 | $ ffscreencast --slist 163 | Available screen recording devices (monitors): 164 | 165 | [2] Capture screen 0 Color LCD: Resolution: 2880 x 1800 Retina 166 | [3] Capture screen 1 S2431W: Resolution: 1920 x 1200 167 | [4] Capture screen 2 Thunderbolt Display: Resolution: 2560 x 1440 168 | 169 | 170 | $ ffscreencast -s2 171 | ``` 172 | 173 | List cameras 174 | 175 | ```shell 176 | $ ffscreencast --clist 177 | Available camera recording devices: 178 | 179 | [0] FaceTime HD Camera (Display) (160x120@29.97 160x120@25 160x120@23.999981 160x120@14.999993 176x144@29.97 176x144@25 176x144@23.999981 176x144@14.999993 320x240@29.97 320x240@25 320x240@23.999981 320x240@14.999993 352x288@29.97 352x288@25 352x288@23.999981 352x288@14.999993 640x480@29.97 640x480@25 640x480@23.999981 640x480@14.999993 960x540@29.97 960x540@25 960x540@23.999981 960x540@14.999993 1024x576@29.97 1024x576@25 1024x576@23.999981 1024x576@14.999993 1280x720@29.97 1280x720@25 1280x720@23.999981 1280x720@14.999993) 180 | 181 | [1] FaceTime HD Camera (1280x720@30 640x480@30 320x240@30) 182 | 183 | ``` 184 | 185 | Start a screencast with camera overlay (only one camera present) 186 | 187 | ```shell 188 | $ ffscreencast -c 189 | ``` 190 | 191 | or select the camera device 192 | 193 | ```shell 194 | $ ffscreencast -c0 195 | ``` 196 | 197 | Show the ffmpeg command for camera recording 198 | 199 | ```shell 200 | $ ffscreencast -c --dry 201 | 202 | ffmpeg -hide_banner -loglevel info -f avfoundation -i "1" -f avfoundation -i "0" -c:v libx264 -crf 0 -preset ultrafast -filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' "/Users/cytopia/Desktop/Screencast 2015-10-06 at 21.28.01.mkv" 203 | 204 | ``` 205 | 206 | ## 4. Screenshots 207 | 208 | Showing screen recording with and without camera overlay. 209 | 210 | ![Screencast](https://raw.githubusercontent.com/cytopia/ffscreencast/master/doc/img/ffscreencast.png) 211 | ![Screencast](https://raw.githubusercontent.com/cytopia/ffscreencast/master/doc/img/ffscreencast2.png) 212 | 213 | ## 5. FAQ 214 | 215 | This section will be updated whenever questions arise that are worth mentioning here 216 | 217 | ### 5.1 How to convert to other formats? 218 | 219 | There is currently no built-in gif support, mainly because it will not produce such a good quality. It is recommended to first create your screencast with the best possible quality and least possible resources (you are obviously going to do something and will not want to consume all your cpu for the encoding) and afterwards convert it to a high quality gif or other formats. 220 | 221 | See [contrib](contrib/) for converters and feel free to add more. 222 | 223 | 224 | ### 5.2 How to enable cursor capturing on OSX? 225 | 226 | By default `ffmpeg` on OSX (using `AVFoundation`) does not capture the mouse (on Linux it does). You can however pass this as a custom option to `ffscreencast` 227 | 228 | In order to capture the mouse pointer itself add the following `--sargs`: 229 | 230 | ```shell 231 | ffscreencast --sargs="-capture_cursor 1" 232 | ``` 233 | 234 | If you also want to *see* when you actually click the mouse, do it like this: 235 | ```shell 236 | ffscreencast --sargs="-capture_cursor 1 -capture_mouse_clicks 1" 237 | ``` 238 | 239 | 240 | 241 | ### 5.3 How to hide the cursor on Linux/BSD? 242 | 243 | By default `ffmpeg` on Linux/BSD (using `x11grab`) does show the mouse pointer by default (OSX does not). You can however pass this as a custom option to `ffscreencast` in order to hide the mouse pointer: 244 | 245 | 246 | ```shell 247 | ffscreencast --sargs="-draw_mouse 0" 248 | ``` 249 | 250 | 251 | 252 | ### 5.4 How to alter the *default* options (config file)? 253 | 254 | When you run `ffscreencast` for the first time, it will create a configuration file in `~/.config/ffscreencast/ffscreencastrc`. Everything specified in this file will be applied when you run `ffscreencast` without any arguments. So if you have your own nice defaults you always need to enter, you can simply add them to the config file. 255 | 256 | ## 6. Todo 257 | 258 | ### 6.1 Bugs 259 | 260 | * [ ] **General:** Sound is still behind one second when using camera overlay 261 | * [X] **OSX:** ~~USB Monitors (see [#1](https://github.com/cytopia/ffscreencast/issues/1))~~ 262 | 263 | ### 6.2 Enhancements 264 | 265 | * [ ] **BSD:** Support for [Free]BSD (needs testing) 266 | * [ ] **Windows:** Support for Windows (via cygwin and dshow) 267 | * [ ] **Linux:** set sound options via cmd (alsa vs pulse) 268 | * [ ] **Linux:** Get default resolution/framerate for camera 269 | * [X] **OSX:** Get default resolution/framerate for camera 270 | * [X] **General:** ~~Set camera resolution via cmd~~ use `--cargs` 271 | * [ ] **General:** Set camera position via cmd 272 | * [ ] **General:** Be able to record one or multiple screens (monitors) 273 | 274 | 275 | 276 | ## 7. Contribution 277 | 278 | Contributors are welcome. 279 | 280 | ## 8. License 281 | 282 | [![license](https://poser.pugx.org/cytopia/ffscreencast/license)](http://opensource.org/licenses/mit) 283 | 284 | ## 9. Version 285 | 286 | For a complete list of verion see [CHANGELOG](CHANGELOG.md) 287 | 288 | ## 10. Awesome 289 | 290 | Added by the following [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) lists: 291 | 292 | * [awesome-cli](https://github.com/aharris88/awesome-cli) 293 | 294 | -------------------------------------------------------------------------------- /bin/ffscreencast: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # @Author: Patrick Plocke 4 | # @GPG 0x28BF179F 5 | # @Licence: MIT 6 | # 7 | # @Description: 8 | # ffscreencast is a shell wrapper for ffmpeg that allows fool-proof screen 9 | # recording via the command line. It will auto-detect all available monitors, 10 | # cameras and microphones and is able to interactively or manually choose the 11 | # desired recording device(s). Additionally ffscreencast will let you overlay 12 | # the camera stream on top of the desktop session. 13 | # 14 | # Besides that ffscreencast can act as an ffmpeg command generator. Every 15 | # available option can also just show the corresponding ffmpeg command instead 16 | # of executing it. 17 | 18 | INFO_AUTHOR="Patrick Plocke " 19 | INFO_GPGKEY="0x28BF179F" 20 | INFO_DATE="2017-03-18" 21 | INFO_LICENSE="MIT" 22 | INFO_VERSION="0.6.4" 23 | INFO_NAME="ffscreencast" 24 | 25 | 26 | 27 | ################################################################################ 28 | # 29 | # VARIABLES 30 | # 31 | ################################################################################ 32 | 33 | ############################################################ 34 | # File name and path 35 | ############################################################ 36 | 37 | # Name of the output file 38 | DATE=$(date +%Y-%m-%d) 39 | TIME=$(date +%H.%M.%S) 40 | NAME="Screencast ${DATE} at ${TIME}" 41 | 42 | # Where to save it 43 | DIR="${HOME}/Desktop" 44 | 45 | 46 | 47 | ############################################################ 48 | # FFmpeg Outout options 49 | ############################################################ 50 | 51 | # Default video container extension 52 | # Alternatively: 'mp4' or 'avi' 53 | OUTPUT_EXT="mkv" 54 | 55 | # Default audio output codec 56 | # Alternatively: 'pcm_s16le' 57 | OUTPUT_ACODEC="libfaac" 58 | 59 | # Default video output codec 60 | # Alternatively: 'libx265' 61 | OUTPUT_VCODEC="libx264" 62 | 63 | 64 | 65 | # Default Screen recording arguments 66 | S_ARGS="" 67 | 68 | # Default audio recording arguments 69 | A_ARGS="-ac 2" 70 | 71 | # Default camera recording arguments 72 | C_ARGS="" 73 | 74 | # Default misc output arguments 75 | O_ARGS="-crf 0 -preset ultrafast" 76 | 77 | 78 | 79 | ############################################################ 80 | # Default selected recording devices 81 | ############################################################ 82 | 83 | # Default recording behavior 84 | RECORD_S="yes" 85 | RECORD_A="no" 86 | RECORD_C="no" 87 | 88 | # What listed device number has been chosen to record? 89 | CHOSEN_S_NUM="" 90 | CHOSEN_A_NUM="" 91 | CHOSEN_C_NUM="" 92 | 93 | 94 | 95 | ############################################################ 96 | # Binaries 97 | ############################################################ 98 | 99 | # Get full path of required tools in case someone has created 100 | # a custom alias on these. 101 | GREP="$(which grep 2> /dev/null)" 102 | AWK="$(which awk 2> /dev/null)" 103 | SED="$(which sed 2> /dev/null)" 104 | FFMPEG="$(which ffmpeg 2> /dev/null)" 105 | UNAME="$(uname 2> /dev/null)" 106 | 107 | 108 | 109 | ############################################################ 110 | # Misc 111 | ############################################################ 112 | 113 | # Program exit codes 114 | EXIT_ERR="1" 115 | EXIT_OK="0" 116 | 117 | 118 | 119 | 120 | ################################################################################ 121 | # 122 | # GENERIC FUNCTIONS 123 | # 124 | ################################################################################ 125 | 126 | 127 | ################################################################################ 128 | # Helper Function 129 | ################################################################################ 130 | 131 | # 132 | # Test if argument is an integer 133 | # 134 | # @param mixed Input value 135 | # @return integer Return code (0: OK | 1: ERR) 136 | isint() { 137 | printf '%d' "$1" >/dev/null 2>&1 && return 0 || return 1; 138 | } 139 | 140 | ################################################################################ 141 | # Config 142 | ################################################################################ 143 | 144 | write_config() { 145 | 146 | local dir 147 | local conf 148 | dir="${HOME}/.config/ffscreencast" 149 | conf="${dir}/ffscreencastrc" 150 | 151 | if [ ! -f "${conf}" ]; then 152 | if [ ! -d "${dir}" ]; then 153 | $(which mkdir) -p "${dir}" 154 | fi 155 | 156 | echo "# ~/.config/ffscreencast/ffscreencastrc" > "${conf}" 157 | echo >> "${conf}" 158 | 159 | echo "# Default video container extension" >> "${conf}" 160 | echo "# Alternatively: 'mp4' or 'avi'" >> "${conf}" 161 | echo "OUTPUT_EXT=\"mkv\"" >> "${conf}" 162 | echo >> "${conf}" 163 | 164 | echo "# Default audio output codec" >> "${conf}" 165 | echo "# Alternatively: 'pcm_s16le'" >> "${conf}" 166 | echo "OUTPUT_ACODEC=\"libfaac\"" >> "${conf}" 167 | echo >> "${conf}" 168 | 169 | echo "# Default video output codec" >> "${conf}" 170 | echo "# Alternatively: 'libx265'" >> "${conf}" 171 | echo "OUTPUT_VCODEC=\"libx264\"" >> "${conf}" 172 | echo >> "${conf}" 173 | 174 | 175 | echo "# Default Screen recording arguments" >> "${conf}" 176 | echo "S_ARGS=\"\"" >> "${conf}" 177 | echo >> "${conf}" 178 | 179 | echo "# Default audio recording arguments" >> "${conf}" 180 | echo "A_ARGS=\"-ac 2\"" >> "${conf}" 181 | echo >> "${conf}" 182 | 183 | echo "# Default camera recording arguments" >> "${conf}" 184 | echo "C_ARGS=\"\"" >> "${conf}" 185 | echo >> "${conf}" 186 | 187 | echo "# Default misc output arguments" >> "${conf}" 188 | echo "O_ARGS=\"-crf 0 -preset ultrafast\"" >> "${conf}" 189 | echo >> "${conf}" 190 | 191 | echo "# Default recording behavior" >> "${conf}" 192 | echo "RECORD_S=\"yes\"" >> "${conf}" 193 | echo "RECORD_A=\"no\"" >> "${conf}" 194 | echo "RECORD_C=\"no\"" >> "${conf}" 195 | echo >> "${conf}" 196 | 197 | echo "# What listed device number has been chosen to record?" >> "${conf}" 198 | echo "CHOSEN_S_NUM=\"\"" >> "${conf}" 199 | echo "CHOSEN_A_NUM=\"\"" >> "${conf}" 200 | echo "CHOSEN_C_NUM=\"\"" >> "${conf}" 201 | echo >> "${conf}" 202 | fi 203 | 204 | } 205 | 206 | 207 | 208 | ################################################################################ 209 | # Info Function 210 | ################################################################################ 211 | 212 | # 213 | # Usage 214 | # 215 | print_usage() { 216 | printf "%s %s %s\n" "Usage:" "${INFO_NAME}" "[-s[num]] [--sargs=] [-a[num]] [--aargs=] [-c[num] [--cargs=] [--oargs=] [-e] [--dry]" 217 | printf "%s %s %s\n" " " "${INFO_NAME}" "--slist [--dry]" 218 | printf "%s %s %s\n" " " "${INFO_NAME}" "--alist [--dry]" 219 | printf "%s %s %s\n" " " "${INFO_NAME}" "--clist [--dry]" 220 | printf "%s %s %s\n" " " "${INFO_NAME}" "--help" 221 | printf "%s %s %s\n" " " "${INFO_NAME}" "--version" 222 | printf "%s %s %s\n" " " "${INFO_NAME}" "--test" 223 | } 224 | 225 | # 226 | # Display program usage 227 | # 228 | print_help() { 229 | 230 | print_usage 231 | echo 232 | 233 | echo "When invoked without any arguments, it will start screen recording" 234 | echo "on the default screen without sound and without camera overlay." 235 | echo 236 | echo "Input options:" 237 | echo "-s[num] (Default) Enable screen capturing [with device number X]." 238 | echo " If no device number is specified it will use the default, if only" 239 | echo " one device is present, otherwise it will ask you to choose one" 240 | echo " Use: -s or -s1" 241 | echo 242 | echo "--sargs= Additional screen arguments." 243 | echo " Specify additional ffmpeg arguments for the screen input device." 244 | echo " Use: --sargs=\"-framerate 30\"" 245 | echo " Default: ''" 246 | echo 247 | echo "-a[num] Enable audio capturing [with device number X]" 248 | echo " If no device number is specified it will use the default, if only" 249 | echo " one device is present, otherwise it will ask you to choose one" 250 | echo " Use: -a or -a1" 251 | echo 252 | echo "--aargs= Additional audio arguments." 253 | echo " Specify additional ffmpeg arguments for the audio input device." 254 | echo " Use: --aargs=\"-ac 1\"" 255 | echo " Default: '-ac 2'" 256 | echo 257 | echo "-c[num] Add camera overlay [with device number X]" 258 | echo " If no device number is specified it will use the default, if only" 259 | echo " one device is present, otherwise it will ask you to choose one" 260 | echo " Use: -c or -c1" 261 | echo 262 | echo "--cargs= Additional camera arguments" 263 | echo " Specify additional ffmpeg arguments for the camera input device." 264 | echo " Use: --cargs=\"-video_size 1280x720\"" 265 | echo " Default: ''" 266 | echo 267 | echo 268 | echo "Output options:" 269 | echo "-e Output video format extension (Default: mkv)" 270 | echo " E.g.: -emkv, or -eavi, or -emp4" 271 | echo 272 | echo "-oargs= Additional output arguments" 273 | echo " Specify additional ffmpeg arguments for the output encoding." 274 | echo " Use: --oargs=\"-crf 0\"" 275 | echo " Default: '-crf 0 -preset ultrafast'" 276 | echo 277 | echo 278 | echo "Behavior options:" 279 | echo "--dry Show the command (without executing)" 280 | echo 281 | echo 282 | echo "List options:" 283 | echo "--list List all devices" 284 | echo "--slist Only list screen capturing devices (monitors)" 285 | echo "--alist Only list audio capturing devices (microphones)" 286 | echo "--clist Only list camera capturing devices (cams)" 287 | echo 288 | echo 289 | echo "System information:" 290 | echo "--help Show this help screen" 291 | echo "--version Show version information" 292 | echo "--test Test requirements" 293 | } 294 | 295 | # 296 | # Display program version and credits 297 | # 298 | print_version() { 299 | printf "Version: %s (%s)\n" "${INFO_VERSION}" "${INFO_DATE}" 300 | printf "Author: %s (%s)\n" "${INFO_AUTHOR}" "${INFO_GPGKEY}" 301 | printf "License: %s\n" "${INFO_LICENSE}" 302 | } 303 | 304 | 305 | # 306 | # Display program requirements 307 | # 308 | print_requirements() { 309 | 310 | #### 1.) Check Operating System 311 | if ! can_run_on_os; then 312 | echo "[ERR] Unsupported operating system." 313 | echo " It currently only works on Linux and OSX." 314 | echo " Sorry ;-)" 315 | else 316 | echo "[OK] Operating system supported: ${UNAME}" 317 | fi 318 | 319 | #### 2.) Check ffmpeg 320 | if ! command -v ffmpeg > /dev/null 2>&1; then 321 | echo "[ERR] ffmpeg not found." 322 | else 323 | echo "[OK] ffmpeg found: $(which ffmpeg)" 324 | fi 325 | 326 | #### 3.) Check OSX 327 | if [ "${UNAME}" = "Darwin" ]; then 328 | if ! can_use_ffmpeg; then 329 | echo "[ERR] [OSX]: AVFoundation not available in ffmpeg." 330 | echo " - Desktop recording not possible." 331 | echo " - Sound recording not possible." 332 | echo " - Camera overlay not possible." 333 | else 334 | echo "[OK] [OSX]: AVFoundation available in ffmpeg." 335 | echo " + Desktop recording possible." 336 | echo " + Sound recording possible." 337 | echo " + Camera overlay possible." 338 | fi 339 | fi 340 | 341 | #### 4.) Check Linux 342 | if [ "${UNAME}" = "Linux" ]; then 343 | if ! can_use_ffmpeg; then 344 | echo "[ERR] [Linux]: x11grab not available in ffmpeg" 345 | else 346 | echo "[OK] [Linux]: x11grab available in ffmpeg" 347 | fi 348 | 349 | if ! can_screen_record; then 350 | echo "[ERR] [Linux]: xdpyinfo not found." 351 | echo " - Desktop recording not possible." 352 | echo 353 | echo " Debian: apt-get install x11-utils" 354 | echo " CentOS: yum install xorg-x11-utils" 355 | echo " Arch: pacman -S xorg-xdpyinfo" 356 | else 357 | echo "[OK] [Linux]: xdpyinfo found: $(which xdpyinfo)" 358 | echo " + Desktop recording possible." 359 | fi 360 | 361 | if ! can_audio_record; then 362 | echo "[WARN] [Linux]: arecord not found." 363 | echo " - Sound recording not possible." 364 | echo 365 | echo " Debian: apt-get install alsa-utils" 366 | echo " CentOS: yum install alsa-utils" 367 | echo " Arch: pacman -S alsa-utils" 368 | else 369 | echo "[OK] [Linux]: arecord found: $(which arecord)" 370 | echo " + Sound recording possible." 371 | fi 372 | 373 | if ! can_camera_record; then 374 | echo "[WARN] [Linux]: v4l2-ctl not found." 375 | echo " - Camera overlay not possible." 376 | echo 377 | echo " Debian: apt-get install v4l-utils" 378 | echo " CentOS: yum install v4l-utils" 379 | echo " Arch: pacman -S v4l-utils" 380 | else 381 | echo "[OK] [Linux]: v4l2-ctl found: $(which v4l2-ctl)" 382 | echo " + Camera overlay possible." 383 | fi 384 | 385 | fi 386 | } 387 | 388 | 389 | 390 | ################################################################################ 391 | # Requirements 392 | ################################################################################ 393 | _CACHE_CAN_USE_OS="no" 394 | can_run_on_os() { 395 | if [ "${_CACHE_CAN_USE_OS}" = "yes" ]; then 396 | return "${EXIT_OK}" 397 | else 398 | if [ "${UNAME}" != "Linux" ] && [ "${UNAME}" != "Darwin" ]; then 399 | # Nope 400 | return "${EXIT_ERR}" 401 | else 402 | # Good :-) 403 | _CACHE_CAN_USE_OS="yes" 404 | return "${EXIT_OK}" 405 | fi 406 | fi 407 | } 408 | 409 | _CACHE_CAN_USE_FFMPEG="no" 410 | can_use_ffmpeg() { 411 | if [ "${_CACHE_CAN_USE_FFMPEG}" = "yes" ]; then 412 | return "${EXIT_OK}" 413 | else 414 | if [ "${UNAME}" = "Darwin" ]; then 415 | if ! ${FFMPEG} -f avfoundation -list_devices true -i "" 2>&1 | $GREP 'AVFoundation input device' > /dev/null 2>&1; then 416 | # Nope, no AVFoundation found in ffmpeg 417 | return "${EXIT_ERR}" 418 | else 419 | # All good :-) 420 | _CACHE_CAN_USE_FFMPEG="yes" 421 | return "${EXIT_OK}" 422 | fi 423 | elif [ "${UNAME}" = "Linux" ]; then 424 | if ! ${FFMPEG} -version 2>&1 | $GREP '\-\-enable-x11grab' > /dev/null 2>&1 \ 425 | && ! ${FFMPEG} -devices 2>&1 | $GREP 'x11grab' > /dev/null 2>&1; then 426 | # Nope, no x11grab found in ffmpeg 427 | return "${EXIT_ERR}" 428 | else 429 | # All good :-) 430 | _CACHE_CAN_USE_FFMPEG="yes" 431 | return "${EXIT_OK}" 432 | fi 433 | fi 434 | 435 | # Hmm, no supported OS found, not good! 436 | return "${EXIT_ERR}" 437 | fi 438 | } 439 | 440 | can_screen_record() { 441 | if [ "${UNAME}" = "Darwin" ]; then 442 | # Note: this is redundant for OSX 443 | if ! can_use_ffmpeg; then 444 | return "${EXIT_ERR}" 445 | else 446 | return "${EXIT_OK}" 447 | fi 448 | elif [ "${UNAME}" = "Linux" ]; then 449 | if ! command -v xdpyinfo > /dev/null 2>&1; then 450 | return "${EXIT_ERR}" 451 | else 452 | return "${EXIT_OK}" 453 | fi 454 | fi 455 | 456 | # Hmm, no supported OS found, not good! 457 | return "${EXIT_ERR}" 458 | } 459 | 460 | can_audio_record() { 461 | # TODO: Check if audio devices are available 462 | 463 | if [ "${UNAME}" = "Darwin" ]; then 464 | # Note: this is redundant for OSX 465 | if ! can_use_ffmpeg; then 466 | return "${EXIT_ERR}" 467 | else 468 | return "${EXIT_OK}" 469 | fi 470 | elif [ "${UNAME}" = "Linux" ]; then 471 | if ! command -v arecord > /dev/null 2>&1; then 472 | return "${EXIT_ERR}" 473 | else 474 | return "${EXIT_OK}" 475 | fi 476 | fi 477 | 478 | # Hmm, no supported OS found, not good! 479 | return "${EXIT_ERR}" 480 | } 481 | 482 | can_camera_record() { 483 | # TODO: Check if camera devices are available 484 | 485 | if [ "${UNAME}" = "Darwin" ]; then 486 | # Note: this is redundant for OSX 487 | if ! can_use_ffmpeg; then 488 | return "${EXIT_ERR}" 489 | else 490 | return "${EXIT_OK}" 491 | fi 492 | elif [ "${UNAME}" = "Linux" ]; then 493 | if ! command -v v4l2-ctl > /dev/null 2>&1; then 494 | return "${EXIT_ERR}" 495 | else 496 | return "${EXIT_OK}" 497 | fi 498 | fi 499 | 500 | # Hmm, no supported OS found, not good! 501 | return "${EXIT_ERR}" 502 | } 503 | 504 | 505 | 506 | 507 | 508 | ################################################################################ 509 | # Get OS dependent device names 510 | ################################################################################ 511 | 512 | # 513 | # Get list of screen devices (monitors) 514 | # 515 | # @param string "dry": Show command only 516 | get_screen_device_names() { 517 | if [ "$(uname)" = "Darwin" ]; then 518 | DEVICE_NAMES="paste <(echo \"\$(ffmpeg -f avfoundation -list_devices true -i '' 2>&1 | $GREP 'AVFoundation input' | $SED -n '/AVFoundation video/,/AVFoundation audio/p' | $GREP -oE '\[[0-9]\].*$' | $GREP 'Capture screen')\") <(echo \"\$(system_profiler SPDisplaysDataType | $SED -n '/^\s.*Displays:$/,\$p' | $GREP -vE '^\s.*Displays:$' | $GREP -E '^\s.*w*:$|Resolution:' | $SED 'N;s/\n/ /' | $SED 's/ \{1,\}/ /g' | $SED 's/^[ \t ]*//;s/[ \t ]*$//')\")" 519 | elif [ "$(uname)" = "Linux" ]; then 520 | DEVICE_NAMES="xdpyinfo | $GREP -A 1 -E '^screen #[0-9]*:' | $GREP -vE '^\-\-' | $SED 'N;s/\n/ /' | $SED 's/dimensions://g' | $SED 's/ \{1,\}/ /g' | $AWK '{printf \"[%d] %s\n\", NR, \$0}'" 521 | fi 522 | if [ "${1}" = "yes" ]; then echo "${DEVICE_NAMES}"; else eval "${DEVICE_NAMES}"; fi 523 | } 524 | 525 | # 526 | # Get list of audio devices (microphones) 527 | # 528 | # @param string "dry": Show command only 529 | get_audio_device_names() { 530 | if [ "$(uname)" = "Darwin" ]; then 531 | DEVICE_NAMES="ffmpeg -f avfoundation -list_devices true -i '' 2>&1 | $GREP 'AVFoundation input' | $SED -n '/AVFoundation audio/,\$p' | $GREP -oE '\[[0-9]\].*$'" 532 | elif [ "$(uname)" = "Linux" ]; then 533 | DEVICE_NAMES="arecord -l | $GREP -E '^card\s[0-9]*:' | $AWK '{printf \"[%d] %s\n\", NR, \$0}'" 534 | fi 535 | if [ "${1}" = "yes" ]; then echo "${DEVICE_NAMES}"; else eval "${DEVICE_NAMES}"; fi 536 | } 537 | 538 | # 539 | # Get list of camera devices 540 | # 541 | # @param string "dry": Show command only 542 | get_camera_device_names() { 543 | if [ "$(uname)" = "Darwin" ]; then 544 | DEVICE_NAMES="ffmpeg -f avfoundation -list_devices true -i '' 2>&1 | $GREP 'AVFoundation input' | $SED -n '/AVFoundation video/,/AVFoundation audio/p' | $GREP -oE '\[[0-9]\].*$' | $GREP 'Camera' | while read line; do tmp=\"\$(echo \$line | $GREP -oE '^\[[0-9]*\]' | $SED 's/\[//' | $SED 's/\]//')\"; reso=\"\$(ffmpeg -t 1 -f avfoundation -r 0.1 -i \$tmp -f mkv - 2>&1 | $GREP '\[avfoundation' | $SED -n '/Supported modes:/,\$p' | $GREP -oE '[0-9]*x[0-9]*\@\[.*fps' | $SED 's/\[[0-9]*\.[0-9]*//' | $SED 's/\s//' | $SED 's/]fps//' | $AWK '{ if(\$0 ~ /\./) sub(\"\\\.*0+\$\",\"\");print}' | tr '\n' ' ' | xargs)\"; echo \"\$line (\$reso)\"; done" 545 | elif [ "$(uname)" = "Linux" ]; then 546 | DEVICE_NAMES="v4l2-ctl --list-devices | $GREP -B 1 '/dev/video' | $GREP -vE '^\-\-' | $SED 'N;s/\n/ /' | $SED 's/ \{1,\}/ /g' | $AWK '{printf \"[%d] %s\n\", NR, \$0}'" 547 | fi 548 | if [ "${1}" = "yes" ]; then echo "${DEVICE_NAMES}"; else eval "${DEVICE_NAMES}"; fi 549 | } 550 | 551 | 552 | 553 | ################################################################################ 554 | # Get device indices 555 | ################################################################################ 556 | 557 | get_screen_device_indices() { 558 | indices="$(get_screen_device_names | $GREP -oE '^\[[0-9]\]' | $SED 's/\[//g' | $SED 's/\]//g')" 559 | echo "$indices" 560 | } 561 | get_audio_device_indices() { 562 | indices="$(get_audio_device_names | $GREP -oE '^\[[0-9]\]' | $SED 's/\[//g' | $SED 's/\]//g')" 563 | echo "$indices" 564 | } 565 | get_camera_device_indices() { 566 | indices="$(get_camera_device_names | $GREP -oE '^\[[0-9]\]' | $SED 's/\[//g' | $SED 's/\]//g')" 567 | echo "$indices" 568 | } 569 | 570 | 571 | 572 | ################################################################################ 573 | # Count devices 574 | ################################################################################ 575 | 576 | count_screen_devices() { 577 | total="$(get_screen_device_names | $GREP -c '')" 578 | echo "$total" 579 | } 580 | count_audio_devices() { 581 | total="$(get_audio_device_names | $GREP -c '')" 582 | echo "$total" 583 | } 584 | count_camera_devices() { 585 | total="$(get_camera_device_names | $GREP -c '')" 586 | echo "$total" 587 | } 588 | 589 | 590 | 591 | ################################################################################ 592 | # Check if device indices exist 593 | ################################################################################ 594 | 595 | # 596 | # Check if screen device exists for given index 597 | # 598 | # @param integer Screen device index 599 | # @return integer Return code (0: OK | 1: ERR) 600 | screen_device_exists() { 601 | index=$1 602 | indices="$(get_screen_device_indices)" 603 | [[ $indices =~ $index ]] && return 0 || return 1 604 | } 605 | 606 | # 607 | # Check if audio device exists for given index 608 | # 609 | # @param integer Screen device index 610 | # @return integer Return code (0: OK | 1: ERR) 611 | audio_device_exists() { 612 | index=$1 613 | indices="$(get_audio_device_indices)" 614 | [[ $indices =~ $index ]] && return 0 || return 1 615 | } 616 | 617 | # 618 | # Check if camera device exists for given index 619 | # 620 | # @param integer Screen device index 621 | # @return integer Return code (0: OK | 1: ERR) 622 | camera_device_exists() { 623 | index=$1 624 | indices="$(get_audio_device_indices)" 625 | [[ $indices =~ $index ]] && return 0 || return 1 626 | } 627 | 628 | 629 | 630 | ################################################################################ 631 | # Interactively choose devices 632 | ################################################################################ 633 | 634 | # 635 | # Interactively (via 'read') choose screen device (monitor) 636 | # Note: Using 'return' instead of echo for chosen device, 637 | # as we need to output success/failer info to the user via 'echo' 638 | # 639 | # @return integer Screen device index 640 | choose_screen_device() { 641 | indices="$(get_screen_device_indices)" 642 | 643 | # List available devices 644 | printf "$(tput setaf 2)Available devices:$(tput sgr0)\n%s\n" "$(get_screen_device_names)" 645 | 646 | # Ask for device number 647 | while true; do 648 | read -r -p "$(tput setaf 2)Enter device number:$(tput sgr0) " dn < /dev/tty 649 | [ ${#dn} -gt 0 ] && [[ $indices =~ $dn ]] && break || echo 'Wrong device number' 650 | done 651 | return "$dn" 652 | } 653 | 654 | # 655 | # Interactively (via 'read') choose audio device (microphone) 656 | # Note: Using 'return' instead of echo for chosen device, 657 | # as we need to output success/failer info to the user via 'echo' 658 | # 659 | # @return integer Audio device index 660 | choose_audio_device() { 661 | indices="$(get_audio_device_indices)" 662 | 663 | # List available devices 664 | printf "$(tput setaf 2)Available devices:$(tput sgr0)\n%s\n" "$(get_audio_device_names)" 665 | 666 | # Ask for device number 667 | while true; do 668 | read -r -p "$(tput setaf 2)Enter device number:$(tput sgr0) " dn < /dev/tty 669 | [ ${#dn} -gt 0 ] && [[ $indices =~ $dn ]] && break || echo 'Wrong device number' 670 | done 671 | return "$dn" 672 | } 673 | 674 | # 675 | # Interactively (via 'read') choose camera device 676 | # Note: Using 'return' instead of echo for chosen device, 677 | # as we need to output success/failer info to the user via 'echo' 678 | # 679 | # @return integer Camera device index 680 | choose_camera_device() { 681 | indices="$(get_camera_device_indices)" 682 | 683 | # List available devices 684 | printf "$(tput setaf 2)Available devices:$(tput sgr0)\n%s\n" "$(get_camera_device_names)" 685 | 686 | # Ask for device number 687 | while true; do 688 | read -r -p "$(tput setaf 2)Enter device number:$(tput sgr0) " dn < /dev/tty 689 | [ ${#dn} -gt 0 ] && [[ $indices =~ $dn ]] && break || echo 'Wrong device number' 690 | done 691 | return "$dn" 692 | } 693 | 694 | 695 | 696 | ################################################################################ 697 | # Get device properties 698 | ################################################################################ 699 | 700 | # 701 | # Get resolution of chosen screen (monitor) 702 | # 703 | # @param integer Screen device index 704 | get_screen_resolution() { 705 | screen_device_index=$1 706 | 707 | if [ "$(uname)" = "Darwin" ]; then 708 | resolution="$(get_screen_device_names | $GREP "\[${screen_device_index}\]" | $GREP -oE '[0-9]*\sx\s[0-9]*' | $SED 's/\s//g')" 709 | elif [ "$(uname)" = "Linux" ]; then 710 | # ShellCheck does not recognize awk, as we are using it in a variable 711 | # shellcheck disable=SC2016 712 | resolution="$(get_screen_device_names | $GREP "\[${screen_device_index}\]" | $GREP -oE '[0-9]*x[0-9]*\spixels' | $AWK '{print $1}')" 713 | fi 714 | 715 | # Format: [0-9].*x[0-9].* (e.g.: 640x480) 716 | echo "${resolution}" 717 | } 718 | 719 | 720 | 721 | # 722 | # Get all resolutions/framerates of a given camera 723 | # Output: WIDTHxHEIGHT@FRAMES 724 | # Example: "1280x720@30 1280x720@25 640x480@30 640x480@10"" 725 | # 726 | get_camera_resolutions() { 727 | 728 | # Linux (v4l): Get all camera resolutions 729 | 730 | echo 731 | # cat v4l.txt | grep -oE '[0-9]+x[0-9]+|[0-9\.]+\sfps' | $SED 's/\sfps//g' | $AWK '{ if($0 ~ /\./) sub("\\.*0+$","");print }' | $AWK '/[0-9]+x[0-9]+/{if (x)print x;x="";}{x=(!x)?$0:x","$0;}END{print x;}' | $SED 's/,/ @ /' | sort -nr | sort -u 732 | 733 | } 734 | 735 | # 736 | # Get first available resolution of chosen camera 737 | # 738 | # @param integer Camera device index 739 | get_default_camera_resolution() { 740 | camera_device_index=$1 741 | 742 | if [ "$(uname)" = "Darwin" ]; then 743 | resolution="$(get_camera_device_names | $GREP "\[${camera_device_index}\]" | $GREP -oE '[0-9]+x[0-9]+' | head -n1)" 744 | # TODO: Add Linux support 745 | elif [ "$(uname)" = "Linux" ]; then 746 | resolution="" 747 | fi 748 | echo "${resolution}" 749 | } 750 | 751 | # 752 | # Get first available framerate of chosen camera for chosen resolution 753 | # 754 | # @param integer Camera device index 755 | # @param string Camera resolution 756 | get_camera_framerate() { 757 | camera_device_index="${1}" 758 | camera_resolution="${2}" 759 | 760 | if [ "$(uname)" = "Darwin" ]; then 761 | framerate="$(get_camera_device_names | $GREP "\[${camera_device_index}\]" | $GREP -oE "${camera_resolution}@[0-9]+(\.)*[0-9]*" | $SED "s/${camera_resolution}@//")" 762 | # TODO: Add Linux support 763 | elif [ "$(uname)" = "Linux" ]; then 764 | framerate="" 765 | fi 766 | echo "${framerate}" 767 | } 768 | 769 | 770 | 771 | 772 | ################################################################################ 773 | # 774 | # MAIN ENTRY POINT 775 | # 776 | ################################################################################ 777 | 778 | 779 | if [ ! -f "${HOME}/.config/ffscreencast/ffscreencastrc" ]; then 780 | # Write default config if it does not exist 781 | write_config 782 | else 783 | # If there is a config, source it as default 784 | . "${HOME}/.config/ffscreencast/ffscreencastrc" 785 | fi 786 | 787 | ############################################################ 788 | # 1.) Evaluate cmd arguments 789 | ############################################################ 790 | 791 | while [ $# -gt 0 ]; do 792 | case "$1" in 793 | 794 | 795 | #### ---- Screen options ---- 796 | 797 | # Screen input device 798 | -s | -s[0-9]*) 799 | RECORD_S="yes" 800 | CHOSEN_S_NUM="$(echo "$1" | $SED 's/^..//g')" 801 | if [ ${#CHOSEN_S_NUM} -gt 0 ]; then 802 | if ! isint "${CHOSEN_S_NUM}" > /dev/null 2>&1; then 803 | echo "Invalid screen number for -s" 804 | exit "${EXIT_ERR}" 805 | fi 806 | fi 807 | ;; 808 | # Screen args 809 | --sargs=*) 810 | S_ARGS="$(echo "$1" | $SED 's/^--sargs=//g')" 811 | ;; 812 | 813 | 814 | #### ---- Audio options ---- 815 | 816 | # Audio recording 817 | -a | -a[0-9]*) 818 | RECORD_A="yes" 819 | CHOSEN_A_NUM="$(echo "$1" | $SED 's/^..//g')" 820 | if [ ${#CHOSEN_A_NUM} -gt 0 ]; then 821 | if ! isint "${CHOSEN_A_NUM}" > /dev/null 2>&1; then 822 | echo "Invalid audio number for -a" 823 | exit "${EXIT_ERR}" 824 | fi 825 | fi 826 | ;; 827 | # Audio args 828 | --aargs=*) 829 | A_ARGS="$(echo "$1" | $SED 's/^--aargs=//g')" 830 | ;; 831 | 832 | 833 | #### ---- Camera options ---- 834 | 835 | # Camera input device 836 | -c | -c[0-9]*) 837 | RECORD_C="yes" 838 | CHOSEN_C_NUM="$(echo "$1" | $SED 's/^..//g')" 839 | if [ ${#CHOSEN_C_NUM} -gt 0 ]; then 840 | if ! isint "${CHOSEN_C_NUM}" > /dev/null 2>&1; then 841 | echo "Invalid camera number for -c" 842 | exit "${EXIT_ERR}" 843 | fi 844 | fi 845 | ;; 846 | # Camera args 847 | --cargs=*) 848 | C_ARGS="$(echo "$1" | $SED 's/^--cargs=//g')" 849 | ;; 850 | 851 | 852 | 853 | #### ---- Output options ---- 854 | 855 | --oargs=*) 856 | O_ARGS="$(echo "$1" | $SED 's/^--oargs=//g')" 857 | ;; 858 | 859 | -e[a-z]*) 860 | OUTPUT_EXT="$(echo "$1" | $SED 's/^..//g')" 861 | ;; 862 | 863 | 864 | 865 | #### ---- Display information options ---- 866 | 867 | --help) 868 | print_help 869 | exit "${EXIT_OK}" 870 | ;; 871 | --version) 872 | print_version 873 | exit "${EXIT_OK}" 874 | ;; 875 | --test) 876 | print_requirements 877 | exit "${EXIT_OK}" 878 | ;; 879 | 880 | #### ---- List devices ---- 881 | 882 | --list) 883 | LIST_ALL_DEVS="yes" 884 | ;; 885 | --slist) 886 | LIST_SCREEN_DEVS="yes" 887 | ;; 888 | --alist) 889 | LIST_AUDIO_DEVS="yes" 890 | ;; 891 | --clist) 892 | LIST_CAMERA_DEVS="yes" 893 | ;; 894 | 895 | 896 | 897 | #### ---- Misc options ---- 898 | 899 | --dry) 900 | l_dry="yes" 901 | ;; 902 | 903 | *) 904 | echo "Invalid argument: '${1}'" 905 | echo "Type '${0} --help' for available options." 906 | exit "${EXIT_ERR}" 907 | ;; 908 | esac 909 | shift 910 | done 911 | 912 | 913 | 914 | ############################################################ 915 | # 2.) Check requirements 916 | ############################################################ 917 | 918 | # Check general requirements 919 | if ! command -v ffmpeg > /dev/null 2>&1; then 920 | echo "FFmpeg not found. Test requirements with:" 921 | echo "${INFO_NAME} --test" 922 | exit "${EXIT_ERR}" 923 | fi 924 | if ! can_run_on_os; then 925 | echo "Operating system not supported. Test requirements with:" 926 | echo "${INFO_NAME} --test" 927 | exit "${EXIT_ERR}" 928 | fi 929 | if ! can_use_ffmpeg; then 930 | echo "FFmpeg does not meet requirements. Test requirements with:" 931 | echo "${INFO_NAME} --test" 932 | exit "${EXIT_ERR}" 933 | fi 934 | 935 | # Check 'selected recordings' requirements 936 | if [ "${RECORD_S}" = "yes" ]; then 937 | if ! can_screen_record; then 938 | echo "Cannot screen record. Test requirements with:" 939 | echo "${INFO_NAME} --test" 940 | exit "${EXIT_ERR}" 941 | fi 942 | fi 943 | if [ "${RECORD_A}" = "yes" ]; then 944 | if ! can_audio_record; then 945 | echo "Cannot audio record. Test requirements with:" 946 | echo "${INFO_NAME} --test" 947 | exit "${EXIT_ERR}" 948 | fi 949 | fi 950 | if [ "${RECORD_C}" = "yes" ]; then 951 | if ! can_camera_record; then 952 | echo "Cannot camera record. Test requirements with:" 953 | echo "${INFO_NAME} --test" 954 | exit "${EXIT_ERR}" 955 | fi 956 | fi 957 | 958 | # Check 'list devices' requirements 959 | if [ "${LIST_SCREEN_DEVS}" = "yes" ]; then 960 | if ! can_screen_record; then 961 | echo "Cannot list screen devices. Test requirements with:" 962 | echo "${INFO_NAME} --test" 963 | exit "${EXIT_ERR}" 964 | fi 965 | fi 966 | if [ "${LIST_AUDIO_DEVS}" = "yes" ]; then 967 | if ! can_audio_record; then 968 | echo "Cannot list audio devices. Test requirements with:" 969 | echo "${INFO_NAME} --test" 970 | exit "${EXIT_ERR}" 971 | fi 972 | fi 973 | if [ "${LIST_CAMERA_DEVS}" = "yes" ]; then 974 | if ! can_camera_record; then 975 | echo "Cannot list camera devices. Test requirements with:" 976 | echo "${INFO_NAME} --test" 977 | exit "${EXIT_ERR}" 978 | fi 979 | fi 980 | if [ "${LIST_ALL_DEVS}" = "yes" ]; then 981 | if ! can_screen_record; then 982 | echo "Cannot list screen devices. Test requirements with:" 983 | echo "${INFO_NAME} --test" 984 | exit "${EXIT_ERR}" 985 | fi 986 | if ! can_camera_record; then 987 | echo "Cannot list camera devices. Test requirements with:" 988 | echo "${INFO_NAME} --test" 989 | exit "${EXIT_ERR}" 990 | fi 991 | if ! can_camera_record; then 992 | echo "Cannot list camera devices. Test requirements with:" 993 | echo "${INFO_NAME} --test" 994 | exit "${EXIT_ERR}" 995 | fi 996 | fi 997 | 998 | 999 | 1000 | ############################################################ 1001 | # 3.) Evaluate List options 1002 | ############################################################ 1003 | 1004 | if [ "${LIST_ALL_DEVS}" = "yes" ]; then 1005 | echo "Available screen recording devices (monitors):" 1006 | echo 1007 | get_screen_device_names "${l_dry}" 1008 | echo 1009 | echo 1010 | 1011 | echo "Available audio recording devices (microphones):" 1012 | echo 1013 | get_audio_device_names "${l_dry}" 1014 | echo 1015 | echo 1016 | 1017 | echo "Available camera recording devices:" 1018 | echo 1019 | get_camera_device_names "${l_dry}" 1020 | exit 0 1021 | fi 1022 | 1023 | if [ "${LIST_SCREEN_DEVS}" = "yes" ]; then 1024 | echo "Available screen recording devices (monitors):" 1025 | echo 1026 | get_screen_device_names "${l_dry}" 1027 | exit 0 1028 | fi 1029 | if [ "${LIST_AUDIO_DEVS}" = "yes" ]; then 1030 | echo "Available audio recording devices (microphones):" 1031 | echo 1032 | get_audio_device_names "${l_dry}" 1033 | exit 0 1034 | fi 1035 | if [ "${LIST_CAMERA_DEVS}" = "yes" ]; then 1036 | echo "Available camera recording devices:" 1037 | echo 1038 | get_camera_device_names "${l_dry}" 1039 | exit 0 1040 | fi 1041 | 1042 | 1043 | 1044 | 1045 | ############################################################ 1046 | # 4.) Build ffmpeg options 1047 | ############################################################ 1048 | 1049 | # Does the user want screen recording? 1050 | if [ "${RECORD_S}" = "yes" ]; then 1051 | 1052 | # If screen device was set via argument list, validate it 1053 | if [ ${#CHOSEN_S_NUM} -gt 0 ]; then 1054 | if ! screen_device_exists "$CHOSEN_S_NUM"; then 1055 | echo "Screen recording device: '${CHOSEN_S_NUM}' does not exist." 1056 | exit -1 1057 | fi 1058 | screen_device="$CHOSEN_S_NUM" 1059 | 1060 | # If screen device was not set via argument list, choose one 1061 | else 1062 | # Use default screen if only one screen exists 1063 | if [ "$(count_screen_devices)" = "1" ]; then 1064 | screen_device=$(get_screen_device_indices) 1065 | else 1066 | choose_screen_device 1067 | screen_device=$? 1068 | fi 1069 | fi 1070 | fi 1071 | 1072 | # Does the user want audio recording? 1073 | if [ "${RECORD_A}" = "yes" ]; then 1074 | 1075 | # If screen device was set via argument list, validate it 1076 | if [ ${#CHOSEN_A_NUM} -gt 0 ]; then 1077 | if ! audio_device_exists "$CHOSEN_A_NUM"; then 1078 | echo "Audio recording device: '${CHOSEN_A_NUM}' does not exist." 1079 | exit -1 1080 | fi 1081 | audio_device="$CHOSEN_A_NUM" 1082 | 1083 | # If audio device was not set via argument list, choose one 1084 | else 1085 | # Use default audio if only one microphone exists 1086 | if [ "$(count_audio_devices)" = "1" ]; then 1087 | audio_device=$(get_audio_device_indices) 1088 | else 1089 | choose_audio_device 1090 | audio_device=$? 1091 | fi 1092 | fi 1093 | fi 1094 | 1095 | # Does the user want camera recording? 1096 | if [ "${RECORD_C}" = "yes" ]; then 1097 | 1098 | # If camera device was set via argument list, validate it 1099 | if [ ${#CHOSEN_C_NUM} -gt 0 ]; then 1100 | if ! camera_device_exists "$CHOSEN_C_NUM"; then 1101 | echo "Camera recording device: '${CHOSEN_C_NUM}' does not exist." 1102 | exit -1 1103 | fi 1104 | camera_device="$CHOSEN_C_NUM" 1105 | 1106 | # If camera device was not set via argument list, choose one 1107 | else 1108 | # Use default camera if only one microphone exists 1109 | if [ "$(count_camera_devices)" = "1" ]; then 1110 | camera_device=$(get_camera_device_indices) 1111 | else 1112 | choose_camera_device 1113 | camera_device=$? 1114 | fi 1115 | fi 1116 | fi 1117 | 1118 | 1119 | 1120 | 1121 | ############################################################ 1122 | # 5. Build os-specific ffmpeg cmd 1123 | ############################################################ 1124 | 1125 | 1126 | # Get screen resolution of selected monitor 1127 | screen_resolution="$(get_screen_resolution "${screen_device}")" 1128 | 1129 | 1130 | # Get camera resolution/framerate of selected camera 1131 | # TODO: Currently only works for OSX 1132 | if [ "${RECORD_C}" = "yes" ]; then 1133 | camera_resolution="$(get_default_camera_resolution "${camera_device}")" 1134 | camera_framerate="$(get_camera_framerate "${camera_device}" "${camera_resolution}")" 1135 | fi 1136 | 1137 | 1138 | 1139 | if [ "${UNAME}" = "Darwin" ]; then 1140 | 1141 | FF_INPUT_FRAMEWORK_SCREEN="avfoundation" 1142 | FF_INPUT_FRAMEWORK_SOUND="avfoundation" 1143 | FF_INPUT_FRAMEWORK_CAMERA="avfoundation" 1144 | 1145 | # Get ffmpeg audio command (if audio was selected) 1146 | if [ "${RECORD_A}" = "yes" ]; then 1147 | audio_device=":${audio_device}" 1148 | fi 1149 | 1150 | if [ "${RECORD_C}" = "yes" ]; then 1151 | camera_fix="-video_size ${camera_resolution} -framerate ${camera_framerate}" 1152 | fi 1153 | 1154 | 1155 | elif [ "${UNAME}" = "Linux" ]; then 1156 | 1157 | FF_INPUT_FRAMEWORK_SCREEN="x11grab" 1158 | FF_INPUT_FRAMEWORK_SOUND="alsa" # TODO: what about pulse?? 1159 | FF_INPUT_FRAMEWORK_CAMERA="v4l2" 1160 | 1161 | # Get ffmpeg audio command (if audio was selected) 1162 | if [ "${RECORD_A}" = "yes" ]; then 1163 | audio_name="$(get_audio_device_names | $GREP "\[${audio_device}\]")" 1164 | # ShellCheck does not recognize awk, as we are using it in a variable 1165 | # shellcheck disable=SC2016 1166 | audio_card="$(echo "${audio_name}" | $GREP -oE 'card\s[0-9]*' | $AWK '{print $2}')" 1167 | # ShellCheck does not recognize awk, as we are using it in a variable 1168 | # shellcheck disable=SC2016 1169 | audio_device="$(echo "${audio_name}" | $GREP -oE 'device\s[0-9]*' | $AWK '{print $2}')" 1170 | audio_device="hw:${audio_card},${audio_device}" 1171 | fi 1172 | 1173 | # Get camera device 1174 | if [ "${RECORD_C}" = "yes" ]; then 1175 | camera_device="$(get_camera_device_names | $GREP "\[${camera_device}\]" | $GREP -oE '/dev/video[0-9]*')" 1176 | camera_fix="" 1177 | fi 1178 | 1179 | screen_device=":0.0" 1180 | 1181 | fi 1182 | 1183 | 1184 | 1185 | #FFMPEG="ffmpeg" 1186 | FFMPEG="${FFMPEG} -hide_banner -loglevel info" 1187 | 1188 | # Video Options 1189 | FFMPEG="${FFMPEG} -thread_queue_size 512" 1190 | 1191 | # If we are able to get the screen resolution, use it 1192 | if [ "$screen_resolution" != "" ]; then 1193 | FFMPEG="${FFMPEG} -f ${FF_INPUT_FRAMEWORK_SCREEN} -video_size ${screen_resolution} ${S_ARGS} -i \"${screen_device}\"" 1194 | else 1195 | FFMPEG="${FFMPEG} -f ${FF_INPUT_FRAMEWORK_SCREEN} ${S_ARGS} -i \"${screen_device}\"" 1196 | fi 1197 | 1198 | # Camera Options (1/2) 1199 | if [ "${RECORD_C}" = "yes" ]; then 1200 | FFMPEG="${FFMPEG} -thread_queue_size 512" 1201 | FFMPEG="${FFMPEG} -f ${FF_INPUT_FRAMEWORK_CAMERA} ${camera_fix} ${C_ARGS} -i \"${camera_device}\"" 1202 | fi 1203 | 1204 | # Sound Options 1205 | if [ "${RECORD_A}" = "yes" ]; then 1206 | FFMPEG="${FFMPEG} -thread_queue_size 512" 1207 | FFMPEG="${FFMPEG} -f ${FF_INPUT_FRAMEWORK_SOUND} ${A_ARGS} -i \"${audio_device}\"" 1208 | FFMPEG="${FFMPEG} -c:a ${OUTPUT_ACODEC}" 1209 | fi 1210 | 1211 | FFMPEG="${FFMPEG} -c:v ${OUTPUT_VCODEC}" 1212 | FFMPEG="${FFMPEG} ${O_ARGS}" 1213 | 1214 | # Camera Options (2/2) 1215 | if [ "${RECORD_C}" = "yes" ]; then 1216 | FFMPEG="${FFMPEG} -filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10'" 1217 | fi 1218 | 1219 | FFMPEG="${FFMPEG} -threads 0" 1220 | FFMPEG="${FFMPEG} \"${DIR}/${NAME}.${OUTPUT_EXT}\"" 1221 | 1222 | 1223 | #### RUN 1224 | if [ "${l_dry}" = "yes" ]; then 1225 | echo "$FFMPEG" 1226 | else 1227 | echo "$FFMPEG" 1228 | eval "$FFMPEG" 1229 | fi 1230 | 1231 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cytopia/ffscreencast", 3 | "description": "ffmpeg screencast with video overlay and multi monitor support.", 4 | "type": "library", 5 | "keywords": ["screencast", "screen recording", "ffmpeg"], 6 | "homepage": "https://github.com/cytopia/ffscreencast", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name" : "Patrick Plocke", 11 | "homepage": "https://github.com/cytopia", 12 | "role": "Developer" 13 | } 14 | ], 15 | "bin": ["bin/ffscreencast", "contrib/ffc-to-gif", "contrib/ffc-to-mp4"] 16 | } 17 | -------------------------------------------------------------------------------- /config/ffscreencastrc: -------------------------------------------------------------------------------- 1 | # ~/.config/ffscreencast/ffscreencastrc 2 | 3 | # Default video container extension 4 | # Alternatively: 'mp4' or 'avi' 5 | OUTPUT_EXT="mkv" 6 | 7 | # Default audio output codec 8 | # Alternatively: 'pcm_s16le' 9 | OUTPUT_ACODEC="libfaac" 10 | 11 | # Default video output codec 12 | # Alternatively: 'libx265' 13 | OUTPUT_VCODEC="libx264" 14 | 15 | # Default Screen recording arguments 16 | S_ARGS="" 17 | 18 | # Default audio recording arguments 19 | A_ARGS="-ac 2" 20 | 21 | # Default camera recording arguments 22 | C_ARGS="" 23 | 24 | # Default misc output arguments 25 | O_ARGS="-crf 0 -preset ultrafast" 26 | 27 | # Default recording behavior 28 | RECORD_S="yes" 29 | RECORD_A="no" 30 | RECORD_C="no" 31 | 32 | # What listed device number has been chosen to record? 33 | CHOSEN_S_NUM="" 34 | CHOSEN_A_NUM="" 35 | CHOSEN_C_NUM="" 36 | 37 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | # FFscreencast converters 2 | 3 | Use the scripts from this folder to convert your videos into other formats. 4 | -------------------------------------------------------------------------------- /contrib/ffc-to-gif: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | INFO_NAME="ffc-to-gif" 4 | INFO_VERSION="0.1" 5 | INFO_DATE="2017-03-18" 6 | 7 | DRY=0 8 | 9 | DEFAULT_WIDTH=1024 10 | DEFAULT_FRAMES=10 11 | 12 | isint() { 13 | printf '%d' "$1" >/dev/null 2>&1 && return 0 || return 1; 14 | } 15 | print_usage() { 16 | echo "Usage: ${INFO_NAME} -i [-w ] [-f ] [--dry] [-o .gif]" 17 | echo " ${INFO_NAME} -h" 18 | echo " ${INFO_NAME} -v" 19 | echo 20 | echo "Convert video to gif using high-quality 2-pass palette conversion" 21 | echo 22 | echo "Required arguments:" 23 | echo " -i Input video to convert" 24 | echo 25 | echo "Optional arguments:" 26 | echo " -w Scale output gif to this width in px. Default: ${DEFAULT_WIDTH}" 27 | echo " -f Create the output gif with this framerate. Default: ${DEFAULT_FRAMES}" 28 | echo " -o .gif Output file. Default: .gif" 29 | echo " --dry Do not do anything, just show the commands" 30 | echo 31 | echo "Info:" 32 | echo " -h Show this help" 33 | echo " -v Show version information" 34 | } 35 | print_version() { 36 | echo "${INFO_NAME} v${INFO_VERSION} (${INFO_DATE})" 37 | } 38 | 39 | 40 | while [ $# -gt 0 ]; do 41 | case "$1" in 42 | -i) 43 | shift 44 | if [ ! -f "${1}" ]; then 45 | echo "Error, -i: No such input file: ${1}" 46 | exit 1 47 | fi 48 | INFILE="${1}" 49 | ;; 50 | -w) 51 | shift 52 | if ! isint "${1}"; then 53 | echo "Error, -w: Not an integer: ${1}" 54 | exit 1 55 | fi 56 | if [ "${1}" -lt "1" ]; then 57 | echo "Error, -w: Must be greater than 0" 58 | exit 1 59 | fi 60 | WIDTH="${1}" 61 | ;; 62 | -f) 63 | shift 64 | if ! isint "${1}"; then 65 | echo "Error, -f: Not an integer: ${1}" 66 | exit 1 67 | fi 68 | if [ "${1}" -lt "1" ]; then 69 | echo "Error, -f: Must be greater than 0" 70 | exit 1 71 | fi 72 | FRAMES="${1}" 73 | ;; 74 | -o) 75 | shift 76 | if [ ! -d "$( dirname "${1}" )" ]; then 77 | echo "Error, -o: Base directory does not exist: $( dirname "${1}" )" 78 | exit 1 79 | fi 80 | OUTFILE="${1}" 81 | ;; 82 | --dry) 83 | DRY=1 84 | ;; 85 | -h) 86 | print_usage 87 | exit 0 88 | ;; 89 | -v) 90 | print_version 91 | exit 0 92 | ;; 93 | *) 94 | echo "Invalid argument: '${1}'" 95 | echo "Type '${0} -h' for available options." 96 | exit 1 97 | ;; 98 | esac 99 | shift 100 | done 101 | 102 | 103 | if [ -z "${INFILE+x}" ]; then 104 | echo "Error, -i: is required." 105 | echo "Usage: ${INFO_NAME} -h" 106 | exit 1 107 | fi 108 | 109 | 110 | if [ -z "${WIDTH+x}" ]; then 111 | WIDTH=${DEFAULT_WIDTH} 112 | fi 113 | if [ -z "${FRAMES+x}" ]; then 114 | FRAMES=${DEFAULT_FRAMES} 115 | fi 116 | if [ -z "${OUTFILE+x}" ]; then 117 | OUTFILE="${INFILE}" 118 | fi 119 | 120 | 121 | PALETTE="$( mktemp --suffix '.png' )" 122 | CMD_RUN_1="ffmpeg -y -i \"${INFILE}\" -vf fps=${FRAMES},scale=${WIDTH}:-1:flags=lanczos,palettegen \"${PALETTE}\" 2>/dev/null" 123 | CMD_RUN_2="ffmpeg -i \"${INFILE}\" -i \"${PALETTE}\" -filter_complex \"fps=${FRAMES},scale=${WIDTH}:-1:flags=lanczos[x];[x][1:v]paletteuse\" \"${OUTFILE}.gif\"" 124 | 125 | if [ "${DRY}" = "0" ]; then 126 | eval "${CMD_RUN_1}" 127 | eval "${CMD_RUN_2}" 128 | else 129 | echo "---- Pass 1/2 ----" 130 | echo "${CMD_RUN_1}" 131 | echo "---- Pass 2/2 ----" 132 | echo "${CMD_RUN_2}" 133 | fi 134 | rm "${PALETTE}" 135 | -------------------------------------------------------------------------------- /contrib/ffc-to-mp4: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | INFO_NAME="ffc-to-mp4" 4 | INFO_VERSION="0.1" 5 | INFO_DATE="2017-03-18" 6 | 7 | DRY=0 8 | 9 | DEFAULT_WIDTH=1024 10 | DEFAULT_BR=5000 11 | 12 | isint() { 13 | printf '%d' "$1" >/dev/null 2>&1 && return 0 || return 1; 14 | } 15 | print_usage() { 16 | echo "Usage: ${INFO_NAME} -i [-w ] [-b ] [--dry] [-o .mp4]" 17 | echo " ${INFO_NAME} -h" 18 | echo " ${INFO_NAME} -v" 19 | echo 20 | echo "Convert video to mp4" 21 | echo 22 | echo "Required arguments:" 23 | echo " -i Input video to convert" 24 | echo 25 | echo "Optional arguments:" 26 | echo " -w Scale output video to this width in px. Default: ${DEFAULT_WIDTH}" 27 | echo " -b Create the output video with this bitrate in kilobytes. Default: ${DEFAULT_BR} (k)" 28 | echo " -o .mp4 Output file. Default: .mp4" 29 | echo " --dry Do not do anything, just show the commands" 30 | echo 31 | echo "Info:" 32 | echo " -h Show this help" 33 | echo " -v Show version information" 34 | } 35 | print_version() { 36 | echo "${INFO_NAME} v${INFO_VERSION} (${INFO_DATE})" 37 | } 38 | 39 | 40 | while [ $# -gt 0 ]; do 41 | case "$1" in 42 | -i) 43 | shift 44 | if [ ! -f "${1}" ]; then 45 | echo "Error, -i: No such input file: ${1}" 46 | exit 1 47 | fi 48 | INFILE="${1}" 49 | ;; 50 | -w) 51 | shift 52 | if ! isint "${1}"; then 53 | echo "Error, -w: Not an integer: ${1}" 54 | exit 1 55 | fi 56 | if [ "${1}" -lt "1" ]; then 57 | echo "Error, -w: Must be greater than 0" 58 | exit 1 59 | fi 60 | WIDTH="${1}" 61 | ;; 62 | -b) 63 | shift 64 | if ! isint "${1}"; then 65 | echo "Error, -b: Not an integer: ${1}" 66 | exit 1 67 | fi 68 | if [ "${1}" -lt "1" ]; then 69 | echo "Error, -b: Must be greater than 0" 70 | exit 1 71 | fi 72 | BITRATE="${1}" 73 | ;; 74 | -o) 75 | shift 76 | if [ ! -d "$( dirname "${1}" )" ]; then 77 | echo "Error, -o: Base directory does not exist: $( dirname "${1}" )" 78 | exit 1 79 | fi 80 | OUTFILE="${1}" 81 | ;; 82 | --dry) 83 | DRY=1 84 | ;; 85 | -h) 86 | print_usage 87 | exit 0 88 | ;; 89 | -v) 90 | print_version 91 | exit 0 92 | ;; 93 | *) 94 | echo "Invalid argument: '${1}'" 95 | echo "Type '${0} -h' for available options." 96 | exit 1 97 | ;; 98 | esac 99 | shift 100 | done 101 | 102 | 103 | if [ -z "${INFILE+x}" ]; then 104 | echo "Error, -i: is required." 105 | echo "Usage: ${INFO_NAME} -h" 106 | exit 1 107 | fi 108 | 109 | 110 | if [ -z "${WIDTH+x}" ]; then 111 | WIDTH=${DEFAULT_WIDTH} 112 | fi 113 | if [ -z "${BITRATE+x}" ]; then 114 | BITRATE=${DEFAULT_BR} 115 | fi 116 | if [ -z "${OUTFILE+x}" ]; then 117 | OUTFILE="${INFILE}" 118 | fi 119 | 120 | 121 | CMD_RUN="ffmpeg -i \"${INFILE}\" -an -c:v mpeg4 -b:v ${BITRATE}k -vf scale=${WIDTH}:-1 \"${OUTFILE}.mp4\"" 122 | 123 | if [ "${DRY}" = "0" ]; then 124 | eval "${CMD_RUN}" 125 | else 126 | echo "${CMD_RUN}" 127 | fi 128 | -------------------------------------------------------------------------------- /doc/img/ffscreencast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytopia/ffscreencast/d77a668ff50cd8ac06cb1c94622ba86988f6dce9/doc/img/ffscreencast.png -------------------------------------------------------------------------------- /doc/img/ffscreencast2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytopia/ffscreencast/d77a668ff50cd8ac06cb1c94622ba86988f6dce9/doc/img/ffscreencast2.png --------------------------------------------------------------------------------