mirror of
https://github.com/ShareX/ShareX.git
synced 2024-07-01 04:30:49 +12:00
Added FFmpeg animated GIF support
This commit is contained in:
parent
7f1daa2480
commit
cd877f5ee2
|
@ -35,7 +35,7 @@ public abstract class ExternalCLIManager : IDisposable
|
|||
public event DataReceivedEventHandler OutputDataReceived;
|
||||
public event DataReceivedEventHandler ErrorDataReceived;
|
||||
|
||||
private Process process = new Process();
|
||||
private Process process;
|
||||
|
||||
public virtual int Open(string path, string args = null)
|
||||
{
|
||||
|
@ -43,26 +43,29 @@ public virtual int Open(string path, string args = null)
|
|||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo(path);
|
||||
psi.UseShellExecute = false;
|
||||
psi.CreateNoWindow = true;
|
||||
psi.RedirectStandardInput = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.Arguments = args;
|
||||
psi.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
psi.StandardOutputEncoding = Encoding.UTF8;
|
||||
psi.StandardErrorEncoding = Encoding.UTF8;
|
||||
using (process = new Process())
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo(path);
|
||||
psi.UseShellExecute = false;
|
||||
psi.CreateNoWindow = true;
|
||||
psi.RedirectStandardInput = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.Arguments = args;
|
||||
psi.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
psi.StandardOutputEncoding = Encoding.UTF8;
|
||||
psi.StandardErrorEncoding = Encoding.UTF8;
|
||||
|
||||
process.EnableRaisingEvents = true;
|
||||
if (psi.RedirectStandardOutput) process.OutputDataReceived += cli_OutputDataReceived;
|
||||
if (psi.RedirectStandardError) process.ErrorDataReceived += cli_ErrorDataReceived;
|
||||
process.StartInfo = psi;
|
||||
process.Start();
|
||||
if (psi.RedirectStandardOutput) process.BeginOutputReadLine();
|
||||
if (psi.RedirectStandardError) process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
return process.ExitCode;
|
||||
process.EnableRaisingEvents = true;
|
||||
if (psi.RedirectStandardOutput) process.OutputDataReceived += cli_OutputDataReceived;
|
||||
if (psi.RedirectStandardError) process.ErrorDataReceived += cli_ErrorDataReceived;
|
||||
process.StartInfo = psi;
|
||||
process.Start();
|
||||
if (psi.RedirectStandardOutput) process.BeginOutputReadLine();
|
||||
if (psi.RedirectStandardError) process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
return process.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
|
|
@ -68,6 +68,8 @@ public enum FFmpegVideoCodec
|
|||
libx264,
|
||||
[Description("VP8 (WebM)")]
|
||||
libvpx,
|
||||
[Description("Animated GIF")]
|
||||
gif,
|
||||
[Description("Xvid")]
|
||||
libxvid,
|
||||
[Description("x265")]
|
||||
|
|
|
@ -65,7 +65,38 @@ private void FFmpegHelper_DataReceived(object sender, DataReceivedEventArgs e)
|
|||
|
||||
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;
|
||||
if (Options.FFmpeg.ShowError && !result)
|
||||
{
|
||||
|
|
|
@ -72,7 +72,7 @@ public bool IsAudioSourceSelected
|
|||
{
|
||||
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)
|
||||
{
|
||||
Helpers.CreateDirectoryIfNotExist(path);
|
||||
|
||||
HardDiskCache hdCache = imgCache as HardDiskCache;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sourceFilePath) && File.Exists(sourceFilePath))
|
||||
|
|
|
@ -67,7 +67,7 @@ public string GetFFmpegCommands()
|
|||
if (FFmpeg.UseCustomCommands && !string.IsNullOrEmpty(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_y$", CaptureArea.Y.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("-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)
|
||||
{
|
||||
|
@ -138,17 +147,34 @@ public string GetFFmpegArgs(bool isCustom = false)
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
case FFmpegVideoCodec.gif:
|
||||
args.AppendFormat("-crf {0} ", FFmpeg.x264_CRF);
|
||||
args.AppendFormat("-preset {0} ", FFmpeg.x264_Preset);
|
||||
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;
|
||||
case FFmpegVideoCodec.libvpx: // https://trac.ffmpeg.org/wiki/Encode/VP8
|
||||
args.AppendFormat("-deadline {0} ", "realtime");
|
||||
|
|
|
@ -177,6 +177,13 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
|||
{
|
||||
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));
|
||||
}
|
||||
else
|
||||
|
@ -266,30 +273,22 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
|||
TrayIcon.Text = "ShareX - " + Resources.ScreenRecordForm_StartRecording_Encoding___;
|
||||
TrayIcon.Icon = Resources.camcorder_pencil.ToIcon();
|
||||
|
||||
string sourceFilePath = path;
|
||||
|
||||
if (outputType == ScreenRecordOutput.GIF)
|
||||
{
|
||||
if (taskSettings.CaptureSettings.RunScreencastCLI)
|
||||
{
|
||||
sourceFilePath = Path.ChangeExtension(Program.ScreenRecorderCacheFilePath, "gif");
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceFilePath = path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
||||
}
|
||||
|
||||
Helpers.CreateDirectoryIfNotExist(sourceFilePath);
|
||||
screenRecorder.EncodingProgressChanged += progress =>
|
||||
{
|
||||
TrayIcon.Text = string.Format("ShareX - {0} ({1}%)", Resources.ScreenRecordForm_StartRecording_Encoding___, progress);
|
||||
};
|
||||
screenRecorder.SaveAsGIF(sourceFilePath, taskSettings.ImageSettings.ImageGIFQuality);
|
||||
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
||||
screenRecorder.EncodingProgressChanged += progress => TrayIcon.Text = string.Format("ShareX - {0} ({1}%)", Resources.ScreenRecordForm_StartRecording_Encoding___, progress);
|
||||
screenRecorder.SaveAsGIF(path, taskSettings.ImageSettings.ImageGIFQuality);
|
||||
}
|
||||
else if (outputType == ScreenRecordOutput.FFmpeg && taskSettings.CaptureSettings.FFmpegOptions.VideoCodec == FFmpegVideoCodec.gif)
|
||||
{
|
||||
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
|
||||
screenRecorder.FFmpegEncodeAsGIF(path);
|
||||
}
|
||||
|
||||
if (taskSettings.CaptureSettings.RunScreencastCLI)
|
||||
{
|
||||
VideoEncoder encoder = Program.Settings.VideoEncoders[taskSettings.CaptureSettings.VideoEncoderSelected];
|
||||
string sourceFilePath = path;
|
||||
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, encoder.OutputExtension));
|
||||
screenRecorder.EncodeUsingCommandLine(encoder, sourceFilePath, path);
|
||||
}
|
||||
|
@ -299,7 +298,8 @@ public void StartRecording(ScreenRecordOutput outputType, TaskSettings taskSetti
|
|||
{
|
||||
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))
|
||||
{
|
||||
File.Delete(screenRecorder.CachePath);
|
||||
|
|
Loading…
Reference in a new issue