2014-05-04 06:55:15 +12:00
#region License Information ( GPL v3 )
/ *
ShareX - A program that allows you to take screenshots and share any file type
2022-01-12 05:32:17 +13:00
Copyright ( c ) 2007 - 2022 ShareX Team
2014-05-04 06:55:15 +12:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
Optionally you can also view the license at < http : //www.gnu.org/licenses/>.
* /
#endregion License Information ( GPL v3 )
2014-12-11 09:25:20 +13:00
using ShareX.HelpersLib ;
2019-11-24 04:06:13 +13:00
using ShareX.MediaLib ;
2014-05-04 06:55:15 +12:00
using System ;
using System.Drawing ;
2014-06-08 07:29:26 +12:00
using System.Globalization ;
2014-05-09 23:45:10 +12:00
using System.IO ;
2014-05-04 06:55:15 +12:00
using System.Text ;
2014-12-11 09:25:20 +13:00
namespace ShareX.ScreenCaptureLib
2014-05-04 06:55:15 +12:00
{
2022-02-07 20:49:09 +13:00
public class ScreenRecordingOptions
2014-05-04 06:55:15 +12:00
{
2018-10-02 07:14:32 +13:00
public bool IsRecording { get ; set ; }
public bool IsLossless { get ; set ; }
public string InputPath { get ; set ; }
2015-01-30 19:50:10 +13:00
public string OutputPath { get ; set ; }
2018-10-02 07:14:32 +13:00
public int FPS { get ; set ; }
2015-01-30 19:50:10 +13:00
public Rectangle CaptureArea { get ; set ; }
public float Duration { get ; set ; }
public bool DrawCursor { get ; set ; }
2016-12-03 05:35:34 +13:00
public FFmpegOptions FFmpeg { get ; set ; } = new FFmpegOptions ( ) ;
2014-05-09 23:45:10 +12:00
2014-06-04 11:18:41 +12:00
public string GetFFmpegCommands ( )
{
2014-06-08 07:29:26 +12:00
string commands ;
2018-10-02 07:14:32 +13:00
if ( IsRecording & & ! string . IsNullOrEmpty ( FFmpeg . VideoSource ) & & FFmpeg . VideoSource . Equals ( "screen-capture-recorder" , StringComparison . InvariantCultureIgnoreCase ) )
2014-06-04 11:18:41 +12:00
{
2014-06-05 02:08:07 +12:00
// https://github.com/rdp/screen-capture-recorder-to-video-windows-free
string registryPath = "Software\\screen-capture-recorder" ;
RegistryHelpers . CreateRegistry ( registryPath , "start_x" , CaptureArea . X ) ;
RegistryHelpers . CreateRegistry ( registryPath , "start_y" , CaptureArea . Y ) ;
RegistryHelpers . CreateRegistry ( registryPath , "capture_width" , CaptureArea . Width ) ;
RegistryHelpers . CreateRegistry ( registryPath , "capture_height" , CaptureArea . Height ) ;
2015-01-30 19:50:10 +13:00
RegistryHelpers . CreateRegistry ( registryPath , "default_max_fps" , 60 ) ;
2014-06-05 02:08:07 +12:00
RegistryHelpers . CreateRegistry ( registryPath , "capture_mouse_default_1" , DrawCursor ? 1 : 0 ) ;
2014-06-04 11:18:41 +12:00
}
2018-10-02 07:14:32 +13:00
if ( ! IsLossless & & FFmpeg . UseCustomCommands & & ! string . IsNullOrEmpty ( FFmpeg . CustomCommands ) )
2014-06-04 11:18:41 +12:00
{
2014-06-09 14:21:18 +12:00
commands = FFmpeg . CustomCommands .
2018-10-02 07:14:32 +13:00
Replace ( "$fps$" , FPS . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) .
2014-06-09 14:21:18 +12:00
Replace ( "$area_x$" , CaptureArea . X . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$area_y$" , CaptureArea . Y . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$area_width$" , CaptureArea . Width . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$area_height$" , CaptureArea . Height . ToString ( ) , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$cursor$" , DrawCursor ? "1" : "0" , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$duration$" , Duration . ToString ( "0.0" , CultureInfo . InvariantCulture ) , StringComparison . InvariantCultureIgnoreCase ) .
Replace ( "$output$" , Path . ChangeExtension ( OutputPath , FFmpeg . Extension ) , StringComparison . InvariantCultureIgnoreCase ) ;
2014-06-08 07:29:26 +12:00
}
else
{
commands = GetFFmpegArgs ( ) ;
2014-06-04 11:18:41 +12:00
}
2014-06-04 23:55:25 +12:00
2014-06-08 07:29:26 +12:00
return commands . Trim ( ) ;
2014-06-04 11:18:41 +12:00
}
2014-06-08 07:29:26 +12:00
public string GetFFmpegArgs ( bool isCustom = false )
2014-05-09 23:45:10 +12:00
{
2018-10-02 07:14:32 +13:00
if ( IsRecording & & ! FFmpeg . IsVideoSourceSelected & & ! FFmpeg . IsAudioSourceSelected )
2014-08-29 20:22:51 +12:00
{
return null ;
}
2014-06-04 09:16:33 +12:00
2014-08-29 20:22:51 +12:00
StringBuilder args = new StringBuilder ( ) ;
2021-11-03 14:07:03 +13:00
args . Append ( "-hide_banner " ) ; // All FFmpeg tools will normally show a copyright notice, build options and library versions. This option can be used to suppress printing this information.
2014-05-13 20:44:35 +12:00
2021-12-24 20:58:26 +13:00
string framerate = isCustom ? "$fps$" : FPS . ToString ( ) ;
2014-06-08 07:29:26 +12:00
2018-10-02 07:14:32 +13:00
if ( IsRecording )
2014-05-13 20:44:35 +12:00
{
2018-10-02 07:14:32 +13:00
if ( FFmpeg . IsVideoSourceSelected )
2014-05-13 20:44:35 +12:00
{
2019-11-24 04:06:13 +13:00
if ( FFmpeg . VideoSource . Equals ( FFmpegCLIManager . SourceGDIGrab , StringComparison . InvariantCultureIgnoreCase ) )
2014-08-29 20:22:51 +12:00
{
2021-12-24 20:58:26 +13:00
string x = isCustom ? "$area_x$" : CaptureArea . X . ToString ( ) ;
string y = isCustom ? "$area_y$" : CaptureArea . Y . ToString ( ) ;
string width = isCustom ? "$area_width$" : CaptureArea . Width . ToString ( ) ;
string height = isCustom ? "$area_height$" : CaptureArea . Height . ToString ( ) ;
string cursor = isCustom ? "$cursor$" : DrawCursor ? "1" : "0" ;
2018-10-02 07:14:32 +13:00
// http://ffmpeg.org/ffmpeg-devices.html#gdigrab
2021-12-24 20:58:26 +13:00
AppendInputDevice ( args , "gdigrab" , false ) ;
args . Append ( $"-framerate {framerate} " ) ;
args . Append ( $"-offset_x {x} " ) ;
args . Append ( $"-offset_y {y} " ) ;
args . Append ( $"-video_size {width}x{height} " ) ;
args . Append ( $"-draw_mouse {cursor} " ) ;
args . Append ( "-i desktop " ) ;
2018-10-02 07:14:32 +13:00
if ( FFmpeg . IsAudioSourceSelected )
{
2021-12-24 20:58:26 +13:00
AppendInputDevice ( args , "dshow" , true ) ;
args . Append ( $"-i audio={Helpers.EscapeCLIText(FFmpeg.AudioSource)} " ) ;
2018-10-02 07:14:32 +13:00
}
2014-08-29 20:22:51 +12:00
}
else
{
2021-12-24 20:58:26 +13:00
AppendInputDevice ( args , "dshow" , FFmpeg . IsAudioSourceSelected ) ;
args . Append ( $"-framerate {framerate} " ) ;
args . Append ( $"-i video={Helpers.EscapeCLIText(FFmpeg.VideoSource)}" ) ;
2018-10-02 07:14:32 +13:00
if ( FFmpeg . IsAudioSourceSelected )
{
2021-12-24 20:58:26 +13:00
args . Append ( $":audio={Helpers.EscapeCLIText(FFmpeg.AudioSource)} " ) ;
2018-10-02 07:14:32 +13:00
}
else
{
args . Append ( " " ) ;
}
2014-08-29 20:22:51 +12:00
}
2014-05-13 20:44:35 +12:00
}
2018-10-02 07:14:32 +13:00
else if ( FFmpeg . IsAudioSourceSelected )
{
2021-12-24 20:58:26 +13:00
AppendInputDevice ( args , "dshow" , true ) ;
args . Append ( $"-i audio={Helpers.EscapeCLIText(FFmpeg.AudioSource)} " ) ;
2018-10-02 07:14:32 +13:00
}
2014-05-13 20:44:35 +12:00
}
2018-10-02 07:14:32 +13:00
else
2014-08-29 20:22:51 +12:00
{
2018-10-02 07:14:32 +13:00
args . Append ( $"-i \" { InputPath } \ " " ) ;
2014-08-29 20:22:51 +12:00
}
2014-05-09 23:45:10 +12:00
2014-05-12 22:04:14 +12:00
if ( ! string . IsNullOrEmpty ( FFmpeg . UserArgs ) )
{
args . Append ( FFmpeg . UserArgs + " " ) ;
}
2014-08-29 20:34:12 +12:00
if ( FFmpeg . IsVideoSourceSelected )
2014-05-09 23:45:10 +12:00
{
2018-10-21 07:59:10 +13:00
if ( IsLossless | | FFmpeg . VideoCodec ! = FFmpegVideoCodec . apng )
2015-06-03 23:32:34 +12:00
{
2018-10-21 07:59:10 +13:00
string videoCodec ;
if ( IsLossless )
{
videoCodec = FFmpegVideoCodec . libx264 . ToString ( ) ;
}
2019-11-29 05:54:07 +13:00
else if ( FFmpeg . VideoCodec = = FFmpegVideoCodec . libvpx_vp9 )
{
videoCodec = "libvpx-vp9" ;
}
2018-10-21 07:59:10 +13:00
else
{
videoCodec = FFmpeg . VideoCodec . ToString ( ) ;
}
2015-06-03 23:32:34 +12:00
2021-12-24 20:58:26 +13:00
args . Append ( $"-c:v {videoCodec} " ) ;
args . Append ( $"-r {framerate} " ) ; // output FPS
2018-10-21 07:59:10 +13:00
}
2014-08-29 20:22:51 +12:00
2018-10-02 07:14:32 +13:00
if ( IsLossless )
2014-08-29 20:22:51 +12:00
{
2021-12-24 20:58:26 +13:00
args . Append ( $"-preset {FFmpegPreset.ultrafast} " ) ;
args . Append ( $"-tune {FFmpegTune.zerolatency} " ) ;
args . Append ( "-qp 0 " ) ;
2018-10-02 07:14:32 +13:00
}
else
{
switch ( FFmpeg . VideoCodec )
{
case FFmpegVideoCodec . libx264 : // https://trac.ffmpeg.org/wiki/Encode/H.264
case FFmpegVideoCodec . libx265 : // https://trac.ffmpeg.org/wiki/Encode/H.265
2021-12-24 20:58:26 +13:00
args . Append ( $"-preset {FFmpeg.x264_Preset} " ) ;
if ( IsRecording ) args . Append ( $"-tune {FFmpegTune.zerolatency} " ) ;
2022-02-07 22:36:02 +13:00
if ( FFmpeg . x264_Use_Bitrate )
{
args . Append ( $"-b:v {FFmpeg.x264_Bitrate}k " ) ;
}
else
{
args . Append ( $"-crf {FFmpeg.x264_CRF} " ) ;
}
2021-12-24 20:58:26 +13:00
args . Append ( "-pix_fmt yuv420p " ) ; // -pix_fmt yuv420p required otherwise can't stream in Chrome
args . Append ( "-movflags +faststart " ) ; // This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer
2018-10-02 07:14:32 +13:00
break ;
case FFmpegVideoCodec . libvpx : // https://trac.ffmpeg.org/wiki/Encode/VP8
2019-11-29 05:54:07 +13:00
case FFmpegVideoCodec . libvpx_vp9 : // https://trac.ffmpeg.org/wiki/Encode/VP9
2021-12-24 20:58:26 +13:00
if ( IsRecording ) args . Append ( "-deadline realtime " ) ;
2022-02-07 21:14:16 +13:00
args . Append ( $"-b:v {FFmpeg.VPx_Bitrate}k " ) ;
2021-12-24 20:58:26 +13:00
args . Append ( "-pix_fmt yuv420p " ) ; // -pix_fmt yuv420p required otherwise causing issues in Chrome related to WebM transparency support
2018-10-02 07:14:32 +13:00
break ;
case FFmpegVideoCodec . libxvid : // https://trac.ffmpeg.org/wiki/Encode/MPEG-4
2022-02-07 21:14:16 +13:00
args . Append ( $"-qscale:v {FFmpeg.XviD_QScale} " ) ;
2018-10-02 07:14:32 +13:00
break ;
case FFmpegVideoCodec . h264_nvenc : // https://trac.ffmpeg.org/wiki/HWAccelIntro#NVENC
case FFmpegVideoCodec . hevc_nvenc :
2022-02-07 21:14:16 +13:00
args . Append ( $"-preset {FFmpeg.NVENC_Preset} " ) ;
args . Append ( $"-b:v {FFmpeg.NVENC_Bitrate}k " ) ;
2021-12-24 20:58:26 +13:00
args . Append ( "-pix_fmt yuv420p " ) ;
args . Append ( "-movflags +faststart " ) ; // This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer
2018-10-02 07:14:32 +13:00
break ;
2018-10-31 12:31:10 +13:00
case FFmpegVideoCodec . h264_amf :
case FFmpegVideoCodec . hevc_amf :
2022-02-07 21:14:16 +13:00
args . Append ( $"-usage {FFmpeg.AMF_Usage} " ) ;
args . Append ( $"-quality {FFmpeg.AMF_Quality} " ) ;
2021-12-24 20:58:26 +13:00
args . Append ( "-pix_fmt yuv420p " ) ;
2018-10-31 12:31:10 +13:00
break ;
2019-03-15 07:33:18 +13:00
case FFmpegVideoCodec . h264_qsv : // https://trac.ffmpeg.org/wiki/Hardware/QuickSync
case FFmpegVideoCodec . hevc_qsv :
2022-02-07 21:14:16 +13:00
args . Append ( $"-preset {FFmpeg.QSV_Preset} " ) ;
args . Append ( $"-b:v {FFmpeg.QSV_Bitrate}k " ) ;
2019-03-15 07:33:18 +13:00
break ;
2019-11-29 04:43:18 +13:00
case FFmpegVideoCodec . libwebp : // https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
2021-12-24 20:58:26 +13:00
args . Append ( "-lossless 0 " ) ;
args . Append ( "-preset default " ) ;
args . Append ( "-loop 0 " ) ;
2019-11-29 04:43:18 +13:00
break ;
case FFmpegVideoCodec . apng :
args . Append ( "-f apng " ) ;
2021-12-24 20:58:26 +13:00
args . Append ( "-plays 0 " ) ;
2019-11-29 04:43:18 +13:00
break ;
2018-10-02 07:14:32 +13:00
}
2014-08-29 20:22:51 +12:00
}
2014-05-09 23:45:10 +12:00
}
2014-08-29 20:34:12 +12:00
if ( FFmpeg . IsAudioSourceSelected )
2014-05-13 20:44:35 +12:00
{
2014-05-15 00:08:51 +12:00
switch ( FFmpeg . AudioCodec )
2014-05-14 23:18:18 +12:00
{
2015-05-16 12:21:59 +12:00
case FFmpegAudioCodec . libvoaacenc : // http://trac.ffmpeg.org/wiki/Encode/AAC
2022-02-07 21:14:16 +13:00
args . Append ( $"-c:a aac -ac 2 -b:a {FFmpeg.AAC_Bitrate}k " ) ; // -ac 2 required otherwise failing with 7.1
2014-05-27 22:36:37 +12:00
break ;
2019-10-22 01:51:32 +13:00
case FFmpegAudioCodec . libopus : // https://www.ffmpeg.org/ffmpeg-codecs.html#libopus-1
2022-02-07 21:14:16 +13:00
args . Append ( $"-c:a libopus -b:a {FFmpeg.Opus_Bitrate}k " ) ;
2019-10-22 01:51:32 +13:00
break ;
2014-05-15 00:08:51 +12:00
case FFmpegAudioCodec . libvorbis : // http://trac.ffmpeg.org/wiki/TheoraVorbisEncodingGuide
2022-02-07 21:14:16 +13:00
args . Append ( $"-c:a libvorbis -qscale:a {FFmpeg.Vorbis_QScale} " ) ;
2014-05-15 00:08:51 +12:00
break ;
2015-05-16 12:21:59 +12:00
case FFmpegAudioCodec . libmp3lame : // http://trac.ffmpeg.org/wiki/Encode/MP3
2022-02-07 21:14:16 +13:00
args . Append ( $"-c:a libmp3lame -qscale:a {FFmpeg.MP3_QScale} " ) ;
2014-05-15 00:08:51 +12:00
break ;
2014-05-14 23:18:18 +12:00
}
2014-05-09 23:45:10 +12:00
}
if ( Duration > 0 )
{
2021-12-24 20:58:26 +13:00
string duration = isCustom ? "$duration$" : Duration . ToString ( "0.0" , CultureInfo . InvariantCulture ) ;
args . Append ( $"-t {duration} " ) ; // duration limit
2014-05-09 23:45:10 +12:00
}
2019-11-17 08:42:35 +13:00
args . Append ( "-y " ) ; // overwrite file
2021-12-24 20:58:26 +13:00
string output = isCustom ? "$output$" : Path . ChangeExtension ( OutputPath , IsLossless ? "mp4" : FFmpeg . Extension ) ;
args . Append ( $"\" { output } \ "" ) ;
2018-10-19 09:15:26 +13:00
2021-12-24 20:58:26 +13:00
return args . ToString ( ) ;
}
2018-10-19 09:15:26 +13:00
2021-12-24 20:58:26 +13:00
private void AppendInputDevice ( StringBuilder args , string inputDevice , bool audioSource )
{
args . Append ( $"-f {inputDevice} " ) ;
args . Append ( "-thread_queue_size 1024 " ) ; // This option sets the maximum number of queued packets when reading from the file or device.
args . Append ( "-rtbufsize 256M " ) ; // Default real time buffer size is 3041280 (3M)
2014-05-09 23:45:10 +12:00
2021-12-24 20:58:26 +13:00
if ( audioSource )
{
args . Append ( "-audio_buffer_size 80 " ) ; // Set audio device buffer size in milliseconds (which can directly impact latency, depending on the device).
}
2014-05-09 23:45:10 +12:00
}
2014-05-09 01:28:46 +12:00
}
2014-05-04 06:55:15 +12:00
}