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
2018-01-02 03:59:14 +13:00
Copyright ( c ) 2007 - 2018 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 ;
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
{
2014-05-09 01:28:46 +12:00
public class ScreencastOptions
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 ( ) ;
args . Append ( "-y " ) ; // -y for overwrite file
2018-10-31 12:33:02 +13:00
args . Append ( "-rtbufsize 150M " ) ; // default real time buffer size was 3041280 (3M)
2014-05-13 20:44:35 +12:00
2015-06-03 23:32:34 +12:00
string fps ;
if ( isCustom )
{
fps = "$fps$" ;
}
else
{
2018-10-02 07:14:32 +13:00
fps = FPS . ToString ( ) ;
2015-06-03 23:32:34 +12:00
}
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
{
2018-10-02 07:14:32 +13:00
if ( FFmpeg . VideoSource . Equals ( FFmpegHelper . SourceGDIGrab , StringComparison . InvariantCultureIgnoreCase ) )
2014-08-29 20:22:51 +12:00
{
2018-10-02 07:14:32 +13:00
// http://ffmpeg.org/ffmpeg-devices.html#gdigrab
args . AppendFormat ( "-f gdigrab -framerate {0} -offset_x {1} -offset_y {2} -video_size {3}x{4} -draw_mouse {5} -i desktop " ,
fps , isCustom ? "$area_x$" : CaptureArea . X . ToString ( ) , isCustom ? "$area_y$" : CaptureArea . Y . ToString ( ) ,
isCustom ? "$area_width$" : CaptureArea . Width . ToString ( ) , isCustom ? "$area_height$" : CaptureArea . Height . ToString ( ) ,
isCustom ? "$cursor$" : DrawCursor ? "1" : "0" ) ;
if ( FFmpeg . IsAudioSourceSelected )
{
args . AppendFormat ( "-f dshow -i audio=\"{0}\" " , FFmpeg . AudioSource ) ;
}
2014-08-29 20:22:51 +12:00
}
else
{
2018-10-02 07:14:32 +13:00
args . AppendFormat ( "-f dshow -framerate {0} -i video=\"{1}\"" , fps , FFmpeg . VideoSource ) ;
if ( FFmpeg . IsAudioSourceSelected )
{
args . AppendFormat ( ":audio=\"{0}\" " , FFmpeg . AudioSource ) ;
}
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 )
{
args . AppendFormat ( "-f dshow -i audio=\"{0}\" " , FFmpeg . AudioSource ) ;
}
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 ( ) ;
}
else
{
videoCodec = FFmpeg . VideoCodec . ToString ( ) ;
}
2015-06-03 23:32:34 +12:00
2018-10-21 07:59:10 +13:00
args . AppendFormat ( "-c:v {0} " , videoCodec ) ;
args . AppendFormat ( "-r {0} " , fps ) ; // output FPS
}
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
{
2018-10-02 07:14:32 +13:00
args . AppendFormat ( "-preset {0} " , FFmpegPreset . ultrafast ) ;
args . AppendFormat ( "-tune {0} " , FFmpegTune . zerolatency ) ;
args . AppendFormat ( "-qp {0} " , 0 ) ;
}
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
args . AppendFormat ( "-preset {0} " , FFmpeg . x264_Preset ) ;
args . AppendFormat ( "-tune {0} " , FFmpegTune . zerolatency ) ;
args . AppendFormat ( "-crf {0} " , FFmpeg . x264_CRF ) ;
args . AppendFormat ( "-pix_fmt {0} " , "yuv420p" ) ; // -pix_fmt yuv420p required otherwise can't stream in Chrome
args . AppendFormat ( "-movflags {0} " , "+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
break ;
case FFmpegVideoCodec . libvpx : // https://trac.ffmpeg.org/wiki/Encode/VP8
args . AppendFormat ( "-deadline {0} " , "realtime" ) ;
args . AppendFormat ( "-b:v {0}k " , FFmpeg . VPx_bitrate ) ;
args . AppendFormat ( "-pix_fmt {0} " , "yuv420p" ) ; // -pix_fmt yuv420p required otherwise causing issues in Chrome related to WebM transparency support
break ;
case FFmpegVideoCodec . libxvid : // https://trac.ffmpeg.org/wiki/Encode/MPEG-4
args . AppendFormat ( "-qscale:v {0} " , FFmpeg . XviD_qscale ) ;
break ;
case FFmpegVideoCodec . h264_nvenc : // https://trac.ffmpeg.org/wiki/HWAccelIntro#NVENC
case FFmpegVideoCodec . hevc_nvenc :
args . AppendFormat ( "-preset {0} " , FFmpeg . NVENC_preset ) ;
args . AppendFormat ( "-b:v {0}k " , FFmpeg . NVENC_bitrate ) ;
args . AppendFormat ( "-pix_fmt {0} " , "yuv420p" ) ;
break ;
2018-10-21 06:06:31 +13:00
case FFmpegVideoCodec . libwebp : // https://www.ffmpeg.org/ffmpeg-codecs.html#libwebp
args . AppendFormat ( "-lossless {0} " , "0" ) ;
args . AppendFormat ( "-preset {0} " , "default" ) ;
args . AppendFormat ( "-loop {0} " , "0" ) ;
break ;
2018-10-21 07:59:10 +13:00
case FFmpegVideoCodec . apng :
args . Append ( "-f apng " ) ;
args . AppendFormat ( "-plays {0} " , "0" ) ;
break ;
2018-10-31 12:31:10 +13:00
case FFmpegVideoCodec . h264_amf :
case FFmpegVideoCodec . hevc_amf :
args . AppendFormat ( "-usage {0} " , FFmpeg . AMF_usage ) ;
args . AppendFormat ( "-quality {0} " , FFmpeg . AMF_quality ) ;
args . AppendFormat ( "-pix_fmt {0} " , "yuv420p" ) ;
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
2016-01-31 20:05:33 +13:00
args . AppendFormat ( "-c:a aac -strict -2 -ac 2 -b:a {0}k " , FFmpeg . AAC_bitrate ) ; // -ac 2 required otherwise failing with 7.1
2014-05-27 22:36:37 +12:00
break ;
2014-05-15 00:08:51 +12:00
case FFmpegAudioCodec . libvorbis : // http://trac.ffmpeg.org/wiki/TheoraVorbisEncodingGuide
2016-01-31 20:05:33 +13:00
args . AppendFormat ( "-c:a libvorbis -qscale:a {0} " , 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
2016-01-31 20:05:33 +13:00
args . AppendFormat ( "-c:a libmp3lame -qscale:a {0} " , 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 )
{
2014-08-29 20:22:51 +12:00
args . AppendFormat ( "-t {0} " , isCustom ? "$duration$" : Duration . ToString ( "0.0" , CultureInfo . InvariantCulture ) ) ; // duration limit
2014-05-09 23:45:10 +12:00
}
2018-10-19 09:15:26 +13:00
string output ;
if ( isCustom )
{
output = "$output$" ;
}
else
{
output = Path . ChangeExtension ( OutputPath , IsLossless ? "mp4" : FFmpeg . Extension ) ;
}
args . AppendFormat ( "\"{0}\"" , output ) ;
2014-05-09 23:45:10 +12:00
return args . ToString ( ) ;
}
2014-05-09 01:28:46 +12:00
}
2014-05-04 06:55:15 +12:00
}