mirror of
https://github.com/ShareX/ShareX.git
synced 2024-10-02 18:26:27 +13:00
Added FFmpeg animated GIF support
This commit is contained in:
parent
7f1daa2480
commit
cd877f5ee2
7 changed files with 114 additions and 44 deletions
|
@ -35,13 +35,15 @@ public abstract class ExternalCLIManager : IDisposable
|
||||||
public event DataReceivedEventHandler OutputDataReceived;
|
public event DataReceivedEventHandler OutputDataReceived;
|
||||||
public event DataReceivedEventHandler ErrorDataReceived;
|
public event DataReceivedEventHandler ErrorDataReceived;
|
||||||
|
|
||||||
private Process process = new Process();
|
private Process process;
|
||||||
|
|
||||||
public virtual int Open(string path, string args = null)
|
public virtual int Open(string path, string args = null)
|
||||||
{
|
{
|
||||||
DebugHelper.WriteLine("CLI: \"{0}\" {1}", path, args);
|
DebugHelper.WriteLine("CLI: \"{0}\" {1}", path, args);
|
||||||
|
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
using (process = new Process())
|
||||||
{
|
{
|
||||||
ProcessStartInfo psi = new ProcessStartInfo(path);
|
ProcessStartInfo psi = new ProcessStartInfo(path);
|
||||||
psi.UseShellExecute = false;
|
psi.UseShellExecute = false;
|
||||||
|
@ -64,6 +66,7 @@ public virtual int Open(string path, string args = null)
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
return process.ExitCode;
|
return process.ExitCode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,8 @@ public enum FFmpegVideoCodec
|
||||||
libx264,
|
libx264,
|
||||||
[Description("VP8 (WebM)")]
|
[Description("VP8 (WebM)")]
|
||||||
libvpx,
|
libvpx,
|
||||||
|
[Description("Animated GIF")]
|
||||||
|
gif,
|
||||||
[Description("Xvid")]
|
[Description("Xvid")]
|
||||||
libxvid,
|
libxvid,
|
||||||
[Description("x265")]
|
[Description("x265")]
|
||||||
|
|
|
@ -65,7 +65,38 @@ private void FFmpegHelper_DataReceived(object sender, DataReceivedEventArgs e)
|
||||||
|
|
||||||
public bool Record()
|
public bool Record()
|
||||||
{
|
{
|
||||||
int errorCode = Open(Options.FFmpeg.CLIPath, Options.GetFFmpegCommands());
|
return Run(Options.FFmpeg.CLIPath, Options.GetFFmpegCommands());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EncodeGIF(string input, string output)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
string palettePath = Path.Combine(Path.GetDirectoryName(Options.FFmpeg.CLIPath), "palette.png");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = Run(Options.FFmpeg.CLIPath, string.Format("-i \"{0}\" -vf palettegen -y \"{1}\"", input, palettePath));
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
result = Run(Options.FFmpeg.CLIPath, string.Format("-i \"{0}\" -i \"{1}\" -lavfi paletteuse -y \"{2}\"", input, palettePath, output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(palettePath))
|
||||||
|
{
|
||||||
|
File.Delete(palettePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Run(string path, string args = null)
|
||||||
|
{
|
||||||
|
int errorCode = Open(path, args);
|
||||||
bool result = errorCode == 0;
|
bool result = errorCode == 0;
|
||||||
if (Options.FFmpeg.ShowError && !result)
|
if (Options.FFmpeg.ShowError && !result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,7 +72,7 @@ public bool IsAudioSourceSelected
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(AudioSource) && !AudioSource.Equals(FFmpegHelper.SourceNone, StringComparison.InvariantCultureIgnoreCase);
|
return !string.IsNullOrEmpty(AudioSource) && !AudioSource.Equals(FFmpegHelper.SourceNone, StringComparison.InvariantCultureIgnoreCase) && VideoCodec != FFmpegVideoCodec.gif;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,8 @@ public void SaveAsGIF(string path, GIFQuality quality)
|
||||||
{
|
{
|
||||||
if (imgCache != null && imgCache is HardDiskCache && !IsRecording)
|
if (imgCache != null && imgCache is HardDiskCache && !IsRecording)
|
||||||
{
|
{
|
||||||
|
Helpers.CreateDirectoryIfNotExist(path);
|
||||||
|
|
||||||
HardDiskCache hdCache = imgCache as HardDiskCache;
|
HardDiskCache hdCache = imgCache as HardDiskCache;
|
||||||
|
|
||||||
using (AnimatedGifCreator gifEncoder = new AnimatedGifCreator(path, delay))
|
using (AnimatedGifCreator gifEncoder = new AnimatedGifCreator(path, delay))
|
||||||
|
@ -223,6 +225,12 @@ public void SaveAsGIF(string path, GIFQuality quality)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool FFmpegEncodeAsGIF(string path)
|
||||||
|
{
|
||||||
|
Helpers.CreateDirectoryIfNotExist(path);
|
||||||
|
return ffmpegCli.EncodeGIF(Options.OutputPath, path);
|
||||||
|
}
|
||||||
|
|
||||||
public void EncodeUsingCommandLine(VideoEncoder encoder, string sourceFilePath, string targetFilePath)
|
public void EncodeUsingCommandLine(VideoEncoder encoder, string sourceFilePath, string targetFilePath)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(sourceFilePath) && File.Exists(sourceFilePath))
|
if (!string.IsNullOrEmpty(sourceFilePath) && File.Exists(sourceFilePath))
|
||||||
|
|
|
@ -67,7 +67,7 @@ public string GetFFmpegCommands()
|
||||||
if (FFmpeg.UseCustomCommands && !string.IsNullOrEmpty(FFmpeg.CustomCommands))
|
if (FFmpeg.UseCustomCommands && !string.IsNullOrEmpty(FFmpeg.CustomCommands))
|
||||||
{
|
{
|
||||||
commands = FFmpeg.CustomCommands.
|
commands = FFmpeg.CustomCommands.
|
||||||
Replace("$fps$", ScreenRecordFPS.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
Replace("$fps$", FFmpeg.VideoCodec == FFmpegVideoCodec.gif ? GIFFPS.ToString() : ScreenRecordFPS.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
||||||
Replace("$area_x$", CaptureArea.X.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
Replace("$area_x$", CaptureArea.X.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
||||||
Replace("$area_y$", CaptureArea.Y.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
Replace("$area_y$", CaptureArea.Y.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
||||||
Replace("$area_width$", CaptureArea.Width.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
Replace("$area_width$", CaptureArea.Width.ToString(), StringComparison.InvariantCultureIgnoreCase).
|
||||||
|
@ -95,7 +95,16 @@ public string GetFFmpegArgs(bool isCustom = false)
|
||||||
args.Append("-y "); // -y for overwrite file
|
args.Append("-y "); // -y for overwrite file
|
||||||
args.Append("-rtbufsize 100M "); // default real time buffer size was 3041280 (3M)
|
args.Append("-rtbufsize 100M "); // default real time buffer size was 3041280 (3M)
|
||||||
|
|
||||||
string fps = isCustom ? "$fps$" : ScreenRecordFPS.ToString();
|
string fps;
|
||||||
|
|
||||||
|
if (isCustom)
|
||||||
|
{
|
||||||
|
fps = "$fps$";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fps = FFmpeg.VideoCodec == FFmpegVideoCodec.gif ? GIFFPS.ToString() : ScreenRecordFPS.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
if (FFmpeg.IsVideoSourceSelected)
|
if (FFmpeg.IsVideoSourceSelected)
|
||||||
{
|
{
|
||||||
|
@ -138,17 +147,34 @@ public string GetFFmpegArgs(bool isCustom = false)
|
||||||
|
|
||||||
if (FFmpeg.IsVideoSourceSelected)
|
if (FFmpeg.IsVideoSourceSelected)
|
||||||
{
|
{
|
||||||
args.AppendFormat("-c:v {0} ", FFmpeg.VideoCodec);
|
string videoCodec;
|
||||||
|
|
||||||
|
switch (FFmpeg.VideoCodec)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
videoCodec = FFmpeg.VideoCodec.ToString();
|
||||||
|
break;
|
||||||
|
case FFmpegVideoCodec.gif:
|
||||||
|
videoCodec = FFmpegVideoCodec.libx264.ToString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.AppendFormat("-c:v {0} ", videoCodec);
|
||||||
args.AppendFormat("-r {0} ", fps); // output FPS
|
args.AppendFormat("-r {0} ", fps); // output FPS
|
||||||
|
|
||||||
switch (FFmpeg.VideoCodec)
|
switch (FFmpeg.VideoCodec)
|
||||||
{
|
{
|
||||||
case FFmpegVideoCodec.libx264: // https://trac.ffmpeg.org/wiki/Encode/H.264
|
case FFmpegVideoCodec.libx264: // https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||||
case FFmpegVideoCodec.libx265: // https://trac.ffmpeg.org/wiki/Encode/H.265
|
case FFmpegVideoCodec.libx265: // https://trac.ffmpeg.org/wiki/Encode/H.265
|
||||||
|
case FFmpegVideoCodec.gif:
|
||||||
args.AppendFormat("-crf {0} ", FFmpeg.x264_CRF);
|
args.AppendFormat("-crf {0} ", FFmpeg.x264_CRF);
|
||||||
args.AppendFormat("-preset {0} ", FFmpeg.x264_Preset);
|
args.AppendFormat("-preset {0} ", FFmpeg.x264_Preset);
|
||||||
args.AppendFormat("-tune {0} ", "zerolatency");
|
args.AppendFormat("-tune {0} ", "zerolatency");
|
||||||
args.Append("-pix_fmt yuv420p "); // -pix_fmt yuv420p required otherwise can't stream in Chrome
|
|
||||||
|
if (FFmpeg.VideoCodec != FFmpegVideoCodec.gif)
|
||||||
|
{
|
||||||
|
args.AppendFormat("-pix_fmt {0} ", "yuv420p"); // -pix_fmt yuv420p required otherwise can't stream in Chrome
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case FFmpegVideoCodec.libvpx: // https://trac.ffmpeg.org/wiki/Encode/VP8
|
case FFmpegVideoCodec.libvpx: // https://trac.ffmpeg.org/wiki/Encode/VP8
|
||||||
args.AppendFormat("-deadline {0} ", "realtime");
|
args.AppendFormat("-deadline {0} ", "realtime");
|
||||||
|
|
|
@ -177,6 +177,13 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
||||||
{
|
{
|
||||||
if (outputType == ScreenRecordOutput.FFmpeg)
|
if (outputType == ScreenRecordOutput.FFmpeg)
|
||||||
{
|
{
|
||||||
|
if (taskSettings.CaptureSettings.FFmpegOptions.VideoCodec == FFmpegVideoCodec.gif)
|
||||||
|
{
|
||||||
|
taskSettings.CaptureSettings.FFmpegOptions.Extension = "mp4";
|
||||||
|
taskSettings.CaptureSettings.FFmpegOptions.x264_CRF = 0;
|
||||||
|
taskSettings.CaptureSettings.FFmpegOptions.x264_Preset = FFmpegPreset.ultrafast;
|
||||||
|
}
|
||||||
|
|
||||||
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, taskSettings.CaptureSettings.FFmpegOptions.Extension));
|
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, taskSettings.CaptureSettings.FFmpegOptions.Extension));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -266,30 +273,22 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
||||||
TrayIcon.Text = "ShareX - " + Resources.ScreenRecordForm_StartRecording_Encoding___;
|
TrayIcon.Text = "ShareX - " + Resources.ScreenRecordForm_StartRecording_Encoding___;
|
||||||
TrayIcon.Icon = Resources.camcorder_pencil.ToIcon();
|
TrayIcon.Icon = Resources.camcorder_pencil.ToIcon();
|
||||||
|
|
||||||
string sourceFilePath = path;
|
|
||||||
|
|
||||||
if (outputType == ScreenRecordOutput.GIF)
|
if (outputType == ScreenRecordOutput.GIF)
|
||||||
{
|
{
|
||||||
if (taskSettings.CaptureSettings.RunScreencastCLI)
|
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
||||||
{
|
screenRecorder.EncodingProgressChanged += progress => TrayIcon.Text = string.Format("ShareX - {0} ({1}%)", Resources.ScreenRecordForm_StartRecording_Encoding___, progress);
|
||||||
sourceFilePath = Path.ChangeExtension(Program.ScreenRecorderCacheFilePath, "gif");
|
screenRecorder.SaveAsGIF(path, taskSettings.ImageSettings.ImageGIFQuality);
|
||||||
}
|
}
|
||||||
else
|
else if (outputType == ScreenRecordOutput.FFmpeg && taskSettings.CaptureSettings.FFmpegOptions.VideoCodec == FFmpegVideoCodec.gif)
|
||||||
{
|
{
|
||||||
sourceFilePath = path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
||||||
}
|
screenRecorder.FFmpegEncodeAsGIF(path);
|
||||||
|
|
||||||
Helpers.CreateDirectoryIfNotExist(sourceFilePath);
|
|
||||||
screenRecorder.EncodingProgressChanged += progress =>
|
|
||||||
{
|
|
||||||
TrayIcon.Text = string.Format("ShareX - {0} ({1}%)", Resources.ScreenRecordForm_StartRecording_Encoding___, progress);
|
|
||||||
};
|
|
||||||
screenRecorder.SaveAsGIF(sourceFilePath, taskSettings.ImageSettings.ImageGIFQuality);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskSettings.CaptureSettings.RunScreencastCLI)
|
if (taskSettings.CaptureSettings.RunScreencastCLI)
|
||||||
{
|
{
|
||||||
VideoEncoder encoder = Program.Settings.VideoEncoders[taskSettings.CaptureSettings.VideoEncoderSelected];
|
VideoEncoder encoder = Program.Settings.VideoEncoders[taskSettings.CaptureSettings.VideoEncoderSelected];
|
||||||
|
string sourceFilePath = path;
|
||||||
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, encoder.OutputExtension));
|
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, encoder.OutputExtension));
|
||||||
screenRecorder.EncodeUsingCommandLine(encoder, sourceFilePath, path);
|
screenRecorder.EncodeUsingCommandLine(encoder, sourceFilePath, path);
|
||||||
}
|
}
|
||||||
|
@ -299,7 +298,8 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
||||||
{
|
{
|
||||||
if (screenRecorder != null)
|
if (screenRecorder != null)
|
||||||
{
|
{
|
||||||
if ((outputType == ScreenRecordOutput.GIF || taskSettings.CaptureSettings.RunScreencastCLI) &&
|
if ((outputType == ScreenRecordOutput.GIF || taskSettings.CaptureSettings.RunScreencastCLI ||
|
||||||
|
(outputType == ScreenRecordOutput.FFmpeg && taskSettings.CaptureSettings.FFmpegOptions.VideoCodec == FFmpegVideoCodec.gif)) &&
|
||||||
!string.IsNullOrEmpty(screenRecorder.CachePath) && File.Exists(screenRecorder.CachePath))
|
!string.IsNullOrEmpty(screenRecorder.CachePath) && File.Exists(screenRecorder.CachePath))
|
||||||
{
|
{
|
||||||
File.Delete(screenRecorder.CachePath);
|
File.Delete(screenRecorder.CachePath);
|
||||||
|
|
Loading…
Reference in a new issue