FFmpegOptionsForm improvements

This commit is contained in:
Jaex 2023-07-19 08:02:43 +03:00
parent 4f4f983b9a
commit 3f6fc79382
8 changed files with 379 additions and 871 deletions

View file

@ -38,12 +38,6 @@ namespace ShareX.MediaLib
{
public class FFmpegCLIManager : ExternalCLIManager
{
public const string SourceNone = "None";
public const string SourceGDIGrab = "GDI grab";
public const string SourceDDAGrab = "Desktop Duplication API";
public const string SourceVideoDevice = "screen-capture-recorder";
public const string SourceAudioDevice = "virtual-audio-capturer";
public const int x264_min = 0;
public const int x264_max = 51;
public const int x265_min = 0;

View file

@ -40,11 +40,9 @@ private void InitializeComponent()
this.tbAACBitrate = new System.Windows.Forms.TrackBar();
this.cbGIFStatsMode = new System.Windows.Forms.ComboBox();
this.cbGIFDither = new System.Windows.Forms.ComboBox();
this.pbAudioCodecWarning = new System.Windows.Forms.PictureBox();
this.pbx264PresetWarning = new System.Windows.Forms.PictureBox();
this.tbOpusBitrate = new System.Windows.Forms.TrackBar();
this.nudGIFBayerScale = new System.Windows.Forms.NumericUpDown();
this.lblCodec = new System.Windows.Forms.Label();
this.cbVideoCodec = new System.Windows.Forms.ComboBox();
this.lblx264Preset = new System.Windows.Forms.Label();
this.lblXvidQscale = new System.Windows.Forms.Label();
@ -99,21 +97,20 @@ private void InitializeComponent()
this.cbAudioSource = new System.Windows.Forms.ComboBox();
this.lblAudioSource = new System.Windows.Forms.Label();
this.cbAudioCodec = new System.Windows.Forms.ComboBox();
this.lblAudioCodec = new System.Windows.Forms.Label();
this.btnHelperDevicesHelp = new System.Windows.Forms.Button();
this.lblHelperDevices = new System.Windows.Forms.Label();
this.btnInstallHelperDevices = new System.Windows.Forms.Button();
this.btnRefreshSources = new System.Windows.Forms.Button();
this.eiFFmpeg = new ShareX.HelpersLib.ExportImportControl();
this.lblCommandLineArgs = new System.Windows.Forms.Label();
this.lblCommandLinePreview = new System.Windows.Forms.Label();
this.cbUseCustomFFmpegPath = new System.Windows.Forms.CheckBox();
this.lblVideoEncoder = new System.Windows.Forms.Label();
this.lblAudioEncoder = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.nudx264CRF)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nudXvidQscale)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.tbVorbis_qscale)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.tbMP3_qscale)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.tbAACBitrate)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pbAudioCodecWarning)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pbx264PresetWarning)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.tbOpusBitrate)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nudGIFBayerScale)).BeginInit();
@ -253,14 +250,6 @@ private void InitializeComponent()
this.ttHelpTip.SetToolTip(this.cbGIFDither, resources.GetString("cbGIFDither.ToolTip"));
this.cbGIFDither.SelectedIndexChanged += new System.EventHandler(this.cbGIFDither_SelectedIndexChanged);
//
// pbAudioCodecWarning
//
this.pbAudioCodecWarning.Image = global::ShareX.ScreenCaptureLib.Properties.Resources.exclamation_button;
resources.ApplyResources(this.pbAudioCodecWarning, "pbAudioCodecWarning");
this.pbAudioCodecWarning.Name = "pbAudioCodecWarning";
this.pbAudioCodecWarning.TabStop = false;
this.ttHelpTip.SetToolTip(this.pbAudioCodecWarning, resources.GetString("pbAudioCodecWarning.ToolTip"));
//
// pbx264PresetWarning
//
this.pbx264PresetWarning.Image = global::ShareX.ScreenCaptureLib.Properties.Resources.exclamation_button;
@ -299,11 +288,6 @@ private void InitializeComponent()
0});
this.nudGIFBayerScale.ValueChanged += new System.EventHandler(this.nudGIFBayerScale_SelectedIndexChanged);
//
// lblCodec
//
resources.ApplyResources(this.lblCodec, "lblCodec");
this.lblCodec.Name = "lblCodec";
//
// cbVideoCodec
//
this.cbVideoCodec.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
@ -747,11 +731,6 @@ private void InitializeComponent()
this.cbAudioCodec.Name = "cbAudioCodec";
this.cbAudioCodec.SelectedIndexChanged += new System.EventHandler(this.cbAudioCodec_SelectedIndexChanged);
//
// lblAudioCodec
//
resources.ApplyResources(this.lblAudioCodec, "lblAudioCodec");
this.lblAudioCodec.Name = "lblAudioCodec";
//
// btnHelperDevicesHelp
//
resources.ApplyResources(this.btnHelperDevicesHelp, "btnHelperDevicesHelp");
@ -771,13 +750,6 @@ private void InitializeComponent()
this.btnInstallHelperDevices.UseVisualStyleBackColor = true;
this.btnInstallHelperDevices.Click += new System.EventHandler(this.btnInstallHelperDevices_Click);
//
// btnRefreshSources
//
resources.ApplyResources(this.btnRefreshSources, "btnRefreshSources");
this.btnRefreshSources.Name = "btnRefreshSources";
this.btnRefreshSources.UseVisualStyleBackColor = true;
this.btnRefreshSources.Click += new System.EventHandler(this.btnRefreshSources_Click);
//
// eiFFmpeg
//
this.eiFFmpeg.DefaultFileName = null;
@ -805,24 +777,33 @@ private void InitializeComponent()
this.cbUseCustomFFmpegPath.UseVisualStyleBackColor = true;
this.cbUseCustomFFmpegPath.CheckedChanged += new System.EventHandler(this.cbUseCustomFFmpegPath_CheckedChanged);
//
// lblVideoEncoder
//
resources.ApplyResources(this.lblVideoEncoder, "lblVideoEncoder");
this.lblVideoEncoder.Name = "lblVideoEncoder";
//
// lblAudioEncoder
//
resources.ApplyResources(this.lblAudioEncoder, "lblAudioEncoder");
this.lblAudioEncoder.Name = "lblAudioEncoder";
//
// FFmpegOptionsForm
//
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.SystemColors.Window;
this.Controls.Add(this.lblAudioEncoder);
this.Controls.Add(this.lblVideoEncoder);
this.Controls.Add(this.cbUseCustomFFmpegPath);
this.Controls.Add(this.cbCustomCommands);
this.Controls.Add(this.lblCommandLinePreview);
this.Controls.Add(this.txtCommandLinePreview);
this.Controls.Add(this.txtUserArgs);
this.Controls.Add(this.lblCommandLineArgs);
this.Controls.Add(this.pbAudioCodecWarning);
this.Controls.Add(this.btnHelperDevicesHelp);
this.Controls.Add(this.cbAudioCodec);
this.Controls.Add(this.lblAudioCodec);
this.Controls.Add(this.lblHelperDevices);
this.Controls.Add(this.cbVideoCodec);
this.Controls.Add(this.lblCodec);
this.Controls.Add(this.tcFFmpegAudioCodecs);
this.Controls.Add(this.btnInstallHelperDevices);
this.Controls.Add(this.tcFFmpegVideoCodecs);
@ -833,7 +814,6 @@ private void InitializeComponent()
this.Controls.Add(this.cbAudioSource);
this.Controls.Add(this.txtFFmpegPath);
this.Controls.Add(this.lblAudioSource);
this.Controls.Add(this.btnRefreshSources);
this.Controls.Add(this.btnCopyPreview);
this.Controls.Add(this.btnTest);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
@ -847,7 +827,6 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)(this.tbVorbis_qscale)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.tbMP3_qscale)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.tbAACBitrate)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pbAudioCodecWarning)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pbx264PresetWarning)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.tbOpusBitrate)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nudGIFBayerScale)).EndInit();
@ -889,7 +868,6 @@ private void InitializeComponent()
private System.Windows.Forms.Label lblx264CRF;
private System.Windows.Forms.NumericUpDown nudx264CRF;
private System.Windows.Forms.ToolTip ttHelpTip;
private System.Windows.Forms.Label lblCodec;
private System.Windows.Forms.ComboBox cbVideoCodec;
private System.Windows.Forms.ComboBox cbx264Preset;
private System.Windows.Forms.Label lblx264Preset;
@ -913,8 +891,6 @@ private void InitializeComponent()
private System.Windows.Forms.ComboBox cbAudioSource;
private System.Windows.Forms.Label lblAudioSource;
private System.Windows.Forms.ComboBox cbAudioCodec;
private System.Windows.Forms.Label lblAudioCodec;
private System.Windows.Forms.Button btnRefreshSources;
private System.Windows.Forms.TrackBar tbVorbis_qscale;
private System.Windows.Forms.Label lblVorbisQuality;
private System.Windows.Forms.TrackBar tbMP3_qscale;
@ -935,7 +911,6 @@ private void InitializeComponent()
private System.Windows.Forms.Button btnHelperDevicesHelp;
private System.Windows.Forms.Label lblHelperDevices;
private System.Windows.Forms.Button btnInstallHelperDevices;
private System.Windows.Forms.PictureBox pbAudioCodecWarning;
private System.Windows.Forms.PictureBox pbx264PresetWarning;
private System.Windows.Forms.TabPage tpNVENC;
private System.Windows.Forms.ComboBox cbNVENCPreset;
@ -964,5 +939,7 @@ private void InitializeComponent()
private System.Windows.Forms.Label lblCommandLineArgs;
private System.Windows.Forms.Label lblCommandLinePreview;
private System.Windows.Forms.CheckBox cbUseCustomFFmpegPath;
private System.Windows.Forms.Label lblVideoEncoder;
private System.Windows.Forms.Label lblAudioEncoder;
}
}

View file

@ -138,7 +138,6 @@ private async Task SettingsLoad()
private async Task RefreshSourcesAsync(bool selectDevices = false)
{
btnRefreshSources.Enabled = false;
DirectShowDevices devices = null;
await Task.Run(() =>
@ -155,46 +154,58 @@ await Task.Run(() =>
if (!IsDisposed)
{
cbVideoSource.Items.Clear();
cbVideoSource.Items.Add(FFmpegCLIManager.SourceNone);
cbVideoSource.Items.Add(FFmpegCLIManager.SourceGDIGrab);
cbVideoSource.Items.Add(FFmpegCaptureDevice.None);
cbVideoSource.Items.Add(FFmpegCaptureDevice.GDIGrab);
if (Helpers.IsWindows10OrGreater())
{
cbVideoSource.Items.Add(FFmpegCLIManager.SourceDDAGrab);
cbVideoSource.Items.Add(FFmpegCaptureDevice.DDAGrab);
}
cbAudioSource.Items.Clear();
cbAudioSource.Items.Add(FFmpegCLIManager.SourceNone);
cbAudioSource.Items.Add(FFmpegCaptureDevice.None);
if (devices != null)
{
cbVideoSource.Items.AddRange(devices.VideoDevices.ToArray());
cbAudioSource.Items.AddRange(devices.AudioDevices.ToArray());
cbVideoSource.Items.AddRange(devices.VideoDevices.Select(x => new FFmpegCaptureDevice(x, $"dshow ({x})")).ToArray());
cbAudioSource.Items.AddRange(devices.AudioDevices.Select(x => new FFmpegCaptureDevice(x, $"dshow ({x})")).ToArray());
}
if (selectDevices && cbVideoSource.Items.Contains(FFmpegCLIManager.SourceVideoDevice))
if (selectDevices && cbVideoSource.Items.Contains(FFmpegCaptureDevice.ScreenCaptureRecorder))
{
Options.FFmpeg.VideoSource = FFmpegCLIManager.SourceVideoDevice;
Options.FFmpeg.VideoSource = FFmpegCaptureDevice.ScreenCaptureRecorder.Value;
}
else if (!cbVideoSource.Items.Contains(Options.FFmpeg.VideoSource))
else if (cbVideoSource.Items.Cast<FFmpegCaptureDevice>().All(x => !x.Value.Equals(Options.FFmpeg.VideoSource, StringComparison.OrdinalIgnoreCase)))
{
Options.FFmpeg.VideoSource = FFmpegCLIManager.SourceGDIGrab;
Options.FFmpeg.VideoSource = FFmpegCaptureDevice.GDIGrab.Value;
}
cbVideoSource.Text = Options.FFmpeg.VideoSource;
if (selectDevices && cbAudioSource.Items.Contains(FFmpegCLIManager.SourceAudioDevice))
foreach (FFmpegCaptureDevice device in cbVideoSource.Items)
{
Options.FFmpeg.AudioSource = FFmpegCLIManager.SourceAudioDevice;
}
else if (!cbAudioSource.Items.Contains(Options.FFmpeg.AudioSource))
if (device.Value.Equals(Options.FFmpeg.VideoSource, StringComparison.OrdinalIgnoreCase))
{
Options.FFmpeg.AudioSource = FFmpegCLIManager.SourceNone;
cbVideoSource.SelectedItem = device;
break;
}
}
cbAudioSource.Text = Options.FFmpeg.AudioSource;
if (selectDevices && cbAudioSource.Items.Contains(FFmpegCaptureDevice.VirtualAudioCapturer))
{
Options.FFmpeg.AudioSource = FFmpegCaptureDevice.VirtualAudioCapturer.Value;
}
else if (cbAudioSource.Items.Cast<FFmpegCaptureDevice>().All(x => !x.Value.Equals(Options.FFmpeg.AudioSource, StringComparison.OrdinalIgnoreCase)))
{
Options.FFmpeg.AudioSource = FFmpegCaptureDevice.None.Value;
}
btnRefreshSources.Enabled = true;
foreach (FFmpegCaptureDevice device in cbAudioSource.Items)
{
if (device.Value.Equals(Options.FFmpeg.AudioSource, StringComparison.OrdinalIgnoreCase))
{
cbAudioSource.SelectedItem = device;
break;
}
}
}
}
@ -210,21 +221,6 @@ private void UpdateUI()
lblVorbisQuality.Text = Resources.FFmpegOptionsForm_UpdateUI_Quality_ + " " + Options.FFmpeg.Vorbis_QScale;
lblMP3Quality.Text = Resources.FFmpegOptionsForm_UpdateUI_Quality_ + " " + Options.FFmpeg.MP3_QScale;
lblOpusQuality.Text = string.Format(Resources.FFmpegOptionsForm_UpdateUI_Bitrate___0_k, Options.FFmpeg.Opus_Bitrate);
bool isValidAudioCodec = true;
FFmpegVideoCodec videoCodec = (FFmpegVideoCodec)cbVideoCodec.SelectedIndex;
if (videoCodec == FFmpegVideoCodec.libvpx)
{
FFmpegAudioCodec audioCodec = (FFmpegAudioCodec)cbAudioCodec.SelectedIndex;
if (audioCodec != FFmpegAudioCodec.libvorbis && audioCodec != FFmpegAudioCodec.libopus)
{
isValidAudioCodec = false;
}
}
pbAudioCodecWarning.Visible = !isValidAudioCodec;
pbx264PresetWarning.Visible = (FFmpegPreset)cbx264Preset.SelectedIndex > FFmpegPreset.fast;
if (!Options.FFmpeg.UseCustomCommands)
@ -260,11 +256,6 @@ private async void buttonFFmpegBrowse_Click(object sender, EventArgs e)
}
}
private async void btnRefreshSources_Click(object sender, EventArgs e)
{
await RefreshSourcesAsync();
}
private void cbVideoSource_SelectedIndexChanged(object sender, EventArgs e)
{
Options.FFmpeg.VideoSource = cbVideoSource.Text;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
#region License Information (GPL v3)
/*
ShareX - A program that allows you to take screenshots and share any file type
Copyright (c) 2007-2023 ShareX Team
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)
namespace ShareX.ScreenCaptureLib
{
public class FFmpegCaptureDevice
{
public string Value { get; set; }
public string Title { get; set; }
public FFmpegCaptureDevice(string value, string title)
{
Value = value;
Title = title;
}
public static FFmpegCaptureDevice None { get; } = new FFmpegCaptureDevice("", "None");
public static FFmpegCaptureDevice GDIGrab { get; } = new FFmpegCaptureDevice("gdigrab", "gdigrab (Graphics Device Interface)");
public static FFmpegCaptureDevice DDAGrab { get; } = new FFmpegCaptureDevice("ddagrab", "ddagrab (Desktop Duplication API)");
public static FFmpegCaptureDevice ScreenCaptureRecorder { get; } = new FFmpegCaptureDevice("screen-capture-recorder", "dshow (screen-capture-recorder)");
public static FFmpegCaptureDevice VirtualAudioCapturer { get; } = new FFmpegCaptureDevice("virtual-audio-capturer", "dshow (virtual-audio-capturer)");
public override string ToString()
{
return Title;
}
}
}

View file

@ -24,8 +24,6 @@ You should have received a copy of the GNU General Public License
#endregion License Information (GPL v3)
using ShareX.HelpersLib;
using ShareX.MediaLib;
using System;
namespace ShareX.ScreenCaptureLib
{
@ -34,8 +32,8 @@ public class FFmpegOptions
// General
public bool OverrideCLIPath { get; set; } = false;
public string CLIPath { get; set; } = "";
public string VideoSource { get; set; } = FFmpegCLIManager.SourceGDIGrab;
public string AudioSource { get; set; } = FFmpegCLIManager.SourceNone;
public string VideoSource { get; set; } = FFmpegCaptureDevice.GDIGrab.Value;
public string AudioSource { get; set; } = FFmpegCaptureDevice.None.Value;
public FFmpegVideoCodec VideoCodec { get; set; } = FFmpegVideoCodec.libx264;
public FFmpegAudioCodec AudioCodec { get; set; } = FFmpegAudioCodec.libvoaacenc;
public string UserArgs { get; set; } = "";
@ -82,7 +80,7 @@ public string Extension
{
get
{
if (!VideoSource.Equals(FFmpegCLIManager.SourceNone, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrEmpty(VideoSource))
{
switch (VideoCodec)
{
@ -108,7 +106,7 @@ public string Extension
return "apng";
}
}
else if (!AudioSource.Equals(FFmpegCLIManager.SourceNone, StringComparison.OrdinalIgnoreCase))
else if (!string.IsNullOrEmpty(AudioSource))
{
switch (AudioCodec)
{
@ -129,10 +127,9 @@ public string Extension
public bool IsSourceSelected => IsVideoSourceSelected || IsAudioSourceSelected;
public bool IsVideoSourceSelected => !string.IsNullOrEmpty(VideoSource) && !VideoSource.Equals(FFmpegCLIManager.SourceNone, StringComparison.OrdinalIgnoreCase);
public bool IsVideoSourceSelected => !string.IsNullOrEmpty(VideoSource);
public bool IsAudioSourceSelected => !string.IsNullOrEmpty(AudioSource) && !AudioSource.Equals(FFmpegCLIManager.SourceNone, StringComparison.OrdinalIgnoreCase) &&
(!IsVideoSourceSelected || !IsAnimatedImage);
public bool IsAudioSourceSelected => !string.IsNullOrEmpty(AudioSource) && (!IsVideoSourceSelected || !IsAnimatedImage);
public bool IsAnimatedImage => VideoCodec == FFmpegVideoCodec.gif || VideoCodec == FFmpegVideoCodec.libwebp || VideoCodec == FFmpegVideoCodec.apng;

View file

@ -24,7 +24,6 @@ You should have received a copy of the GNU General Public License
#endregion License Information (GPL v3)
using ShareX.HelpersLib;
using ShareX.MediaLib;
using System;
using System.Drawing;
using System.Globalization;
@ -51,7 +50,8 @@ public string GetFFmpegCommands()
{
string commands;
if (IsRecording && !string.IsNullOrEmpty(FFmpeg.VideoSource) && FFmpeg.VideoSource.Equals("screen-capture-recorder", StringComparison.OrdinalIgnoreCase))
if (IsRecording && !string.IsNullOrEmpty(FFmpeg.VideoSource) &&
FFmpeg.VideoSource.Equals(FFmpegCaptureDevice.ScreenCaptureRecorder.Value, StringComparison.OrdinalIgnoreCase))
{
// https://github.com/rdp/screen-capture-recorder-to-video-windows-free
string registryPath = "Software\\screen-capture-recorder";
@ -99,7 +99,7 @@ public string GetFFmpegArgs(bool isCustom = false)
{
if (FFmpeg.IsVideoSourceSelected)
{
if (FFmpeg.VideoSource.Equals(FFmpegCLIManager.SourceGDIGrab, StringComparison.OrdinalIgnoreCase))
if (FFmpeg.VideoSource.Equals(FFmpegCaptureDevice.GDIGrab.Value, StringComparison.OrdinalIgnoreCase))
{
string x = isCustom ? "$area_x$" : CaptureArea.X.ToString();
string y = isCustom ? "$area_y$" : CaptureArea.Y.ToString();
@ -107,7 +107,7 @@ public string GetFFmpegArgs(bool isCustom = false)
string height = isCustom ? "$area_height$" : CaptureArea.Height.ToString();
string cursor = isCustom ? "$cursor$" : DrawCursor ? "1" : "0";
// http://ffmpeg.org/ffmpeg-devices.html#gdigrab
// https://ffmpeg.org/ffmpeg-devices.html#gdigrab
AppendInputDevice(args, "gdigrab", false);
args.Append($"-framerate {framerate} ");
args.Append($"-offset_x {x} ");
@ -122,7 +122,7 @@ public string GetFFmpegArgs(bool isCustom = false)
args.Append($"-i audio={Helpers.EscapeCLIText(FFmpeg.AudioSource)} ");
}
}
else if (FFmpeg.VideoSource.Equals(FFmpegCLIManager.SourceDDAGrab, StringComparison.OrdinalIgnoreCase))
else if (FFmpeg.VideoSource.Equals(FFmpegCaptureDevice.DDAGrab.Value, StringComparison.OrdinalIgnoreCase))
{
Screen[] screens = Screen.AllScreens.OrderBy(x => !x.Primary).ToArray();
int monitorIndex = 0;
@ -170,6 +170,7 @@ public string GetFFmpegArgs(bool isCustom = false)
}
else
{
// https://ffmpeg.org/ffmpeg-devices.html#dshow
AppendInputDevice(args, "dshow", FFmpeg.IsAudioSourceSelected);
args.Append($"-framerate {framerate} ");
args.Append($"-i video={Helpers.EscapeCLIText(FFmpeg.VideoSource)}");

View file

@ -156,6 +156,7 @@
<Compile Include="Helpers\ImageEditorScrollbar.cs" />
<Compile Include="Helpers\ScrollbarManager.cs" />
<Compile Include="Helpers\ScrollingCaptureManager.cs" />
<Compile Include="ScreenRecording\FFmpegCaptureDevice.cs" />
<Compile Include="Shapes\AnnotationOptions.cs" />
<Compile Include="Enums.cs" />
<Compile Include="Forms\RegionCaptureTransparentForm.cs">