Added two pass encoding support

This commit is contained in:
Jaex 2018-10-01 21:14:32 +03:00
parent 64822000be
commit 41ed5a38e2
5 changed files with 111 additions and 80 deletions

View file

@ -92,6 +92,15 @@ protected void OnRecordingStarted()
}
}
public bool EncodeVideo(string input, string output)
{
Options.IsRecording = false;
Options.IsLossless = false;
Options.InputPath = input;
Options.OutputPath = output;
return Run(Options.FFmpeg.FFmpegPath, Options.GetFFmpegCommands());
}
public bool EncodeGIF(string input, string output, string tempFolder)
{
bool result;

View file

@ -109,7 +109,7 @@ public ScreenRecorder(ScreenRecordOutput outputType, ScreencastOptions options,
throw new Exception("Screen recorder cache path is empty.");
}
FPS = outputType == ScreenRecordOutput.GIF ? options.GIFFPS : options.ScreenRecordFPS;
FPS = options.FPS;
DurationSeconds = options.Duration;
CaptureRectangle = captureRectangle;
CachePath = options.OutputPath;
@ -230,6 +230,14 @@ public void SaveAsGIF(string path, GIFQuality quality)
}
}
public bool FFmpegEncodeVideo(string input, string output)
{
Helpers.CreateDirectoryFromFilePath(output);
bool result = ffmpegCli.EncodeVideo(input, output);
//DebugHelper.WriteLine("Video encoding result:\nInput file size: {0}\nOutput file size: {1}", new FileInfo(input).Length.ToSizeString(), new FileInfo(output).Length.ToSizeString());
return result;
}
public bool FFmpegEncodeAsGIF(string sourceFilePath, string targetFilePath, string tempFolder)
{
Helpers.CreateDirectoryFromFilePath(targetFilePath);

View file

@ -34,10 +34,11 @@ namespace ShareX.ScreenCaptureLib
{
public class ScreencastOptions
{
public ScreenRecordOutput OutputType { get; set; }
public bool IsRecording { get; set; }
public bool IsLossless { get; set; }
public string InputPath { get; set; }
public string OutputPath { get; set; }
public int GIFFPS { get; set; }
public int ScreenRecordFPS { get; set; }
public int FPS { get; set; }
public Rectangle CaptureArea { get; set; }
public float Duration { get; set; }
public bool DrawCursor { get; set; }
@ -47,7 +48,7 @@ public string GetFFmpegCommands()
{
string commands;
if (!string.IsNullOrEmpty(FFmpeg.VideoSource) && FFmpeg.VideoSource.Equals("screen-capture-recorder", StringComparison.InvariantCultureIgnoreCase))
if (IsRecording && !string.IsNullOrEmpty(FFmpeg.VideoSource) && FFmpeg.VideoSource.Equals("screen-capture-recorder", StringComparison.InvariantCultureIgnoreCase))
{
// https://github.com/rdp/screen-capture-recorder-to-video-windows-free
string registryPath = "Software\\screen-capture-recorder";
@ -59,10 +60,10 @@ public string GetFFmpegCommands()
RegistryHelpers.CreateRegistry(registryPath, "capture_mouse_default_1", DrawCursor ? 1 : 0);
}
if (FFmpeg.UseCustomCommands && !string.IsNullOrEmpty(FFmpeg.CustomCommands))
if (!IsLossless && FFmpeg.UseCustomCommands && !string.IsNullOrEmpty(FFmpeg.CustomCommands))
{
commands = FFmpeg.CustomCommands.
Replace("$fps$", FFmpeg.VideoCodec == FFmpegVideoCodec.gif ? GIFFPS.ToString() : ScreenRecordFPS.ToString(), StringComparison.InvariantCultureIgnoreCase).
Replace("$fps$", FPS.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).
@ -81,7 +82,7 @@ public string GetFFmpegCommands()
public string GetFFmpegArgs(bool isCustom = false)
{
if (!FFmpeg.IsVideoSourceSelected && !FFmpeg.IsAudioSourceSelected)
if (IsRecording && !FFmpeg.IsVideoSourceSelected && !FFmpeg.IsAudioSourceSelected)
{
return null;
}
@ -98,41 +99,48 @@ public string GetFFmpegArgs(bool isCustom = false)
}
else
{
fps = FFmpeg.VideoCodec == FFmpegVideoCodec.gif ? GIFFPS.ToString() : ScreenRecordFPS.ToString();
fps = FPS.ToString();
}
if (FFmpeg.IsVideoSourceSelected)
if (IsRecording)
{
if (FFmpeg.VideoSource.Equals(FFmpegHelper.SourceGDIGrab, StringComparison.InvariantCultureIgnoreCase))
if (FFmpeg.IsVideoSourceSelected)
{
// 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)
if (FFmpeg.VideoSource.Equals(FFmpegHelper.SourceGDIGrab, StringComparison.InvariantCultureIgnoreCase))
{
args.AppendFormat("-f dshow -i audio=\"{0}\" ", FFmpeg.AudioSource);
}
}
else
{
args.AppendFormat("-f dshow -framerate {0} -i video=\"{1}\"", fps, FFmpeg.VideoSource);
// 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(":audio=\"{0}\" ", FFmpeg.AudioSource);
if (FFmpeg.IsAudioSourceSelected)
{
args.AppendFormat("-f dshow -i audio=\"{0}\" ", FFmpeg.AudioSource);
}
}
else
{
args.Append(" ");
args.AppendFormat("-f dshow -framerate {0} -i video=\"{1}\"", fps, FFmpeg.VideoSource);
if (FFmpeg.IsAudioSourceSelected)
{
args.AppendFormat(":audio=\"{0}\" ", FFmpeg.AudioSource);
}
else
{
args.Append(" ");
}
}
}
else if (FFmpeg.IsAudioSourceSelected)
{
args.AppendFormat("-f dshow -i audio=\"{0}\" ", FFmpeg.AudioSource);
}
}
else if (FFmpeg.IsAudioSourceSelected)
else
{
args.AppendFormat("-f dshow -i audio=\"{0}\" ", FFmpeg.AudioSource);
args.Append($"-i \"{InputPath}\" ");
}
if (!string.IsNullOrEmpty(FFmpeg.UserArgs))
@ -144,48 +152,51 @@ public string GetFFmpegArgs(bool isCustom = false)
{
string videoCodec;
switch (FFmpeg.VideoCodec)
if (IsLossless)
{
default:
videoCodec = FFmpeg.VideoCodec.ToString();
break;
case FFmpegVideoCodec.gif:
videoCodec = FFmpegVideoCodec.libx264.ToString();
break;
videoCodec = FFmpegVideoCodec.libx264.ToString();
}
else
{
videoCodec = FFmpeg.VideoCodec.ToString();
}
args.AppendFormat("-c:v {0} ", videoCodec);
args.AppendFormat("-r {0} ", fps); // output FPS
switch (FFmpeg.VideoCodec)
if (IsLossless)
{
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;
case FFmpegVideoCodec.gif:
args.AppendFormat("-preset {0} ", FFmpegPreset.ultrafast);
args.AppendFormat("-tune {0} ", FFmpegTune.zerolatency);
args.AppendFormat("-qp {0} ", 0);
break;
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;
}
}
}

View file

@ -1050,9 +1050,9 @@ private void btnScreenRecorderFFmpegOptions_Click(object sender, EventArgs e)
{
ScreencastOptions options = new ScreencastOptions
{
IsRecording = true,
FFmpeg = TaskSettings.CaptureSettings.FFmpegOptions,
ScreenRecordFPS = TaskSettings.CaptureSettings.ScreenRecordFPS,
GIFFPS = TaskSettings.CaptureSettings.GIFFPS,
FPS = TaskSettings.CaptureSettings.ScreenRecordFPS,
Duration = TaskSettings.CaptureSettings.ScreenRecordFixedDuration ? TaskSettings.CaptureSettings.ScreenRecordDuration : 0,
OutputPath = "output.mp4",
CaptureArea = Screen.PrimaryScreen.Bounds,

View file

@ -88,6 +88,7 @@ private static void StartRecording(ScreenRecordOutput outputType, TaskSettings t
else
{
DebugHelper.WriteLine("Starting screen recording. FPS: {0}", taskSettings.CaptureSettings.GIFFPS);
taskSettings.CaptureSettings.FFmpegOptions.VideoCodec = FFmpegVideoCodec.gif;
}
if (taskSettings.CaptureSettings.RunScreencastCLI)
@ -108,12 +109,6 @@ private static void StartRecording(ScreenRecordOutput outputType, TaskSettings t
}
}
if (outputType == ScreenRecordOutput.GIF)
{
taskSettings.CaptureSettings.FFmpegOptions.VideoCodec = FFmpegVideoCodec.gif;
taskSettings.CaptureSettings.FFmpegOptions.UseCustomCommands = false;
}
if (!TaskHelpers.CheckFFmpeg(taskSettings))
{
return;
@ -218,9 +213,10 @@ private static void StartRecording(ScreenRecordOutput outputType, TaskSettings t
{
ScreencastOptions options = new ScreencastOptions()
{
IsRecording = true,
IsLossless = outputType == ScreenRecordOutput.GIF || taskSettings.CaptureSettings.ScreenRecordTwoPassEncoding,
FFmpeg = taskSettings.CaptureSettings.FFmpegOptions,
ScreenRecordFPS = taskSettings.CaptureSettings.ScreenRecordFPS,
GIFFPS = taskSettings.CaptureSettings.GIFFPS,
FPS = outputType == ScreenRecordOutput.GIF ? taskSettings.CaptureSettings.GIFFPS : taskSettings.CaptureSettings.ScreenRecordFPS,
Duration = duration,
OutputPath = path,
CaptureArea = captureRectangle,
@ -253,19 +249,26 @@ private static void StartRecording(ScreenRecordOutput outputType, TaskSettings t
{
recordForm.ChangeState(ScreenRecordState.AfterStop);
string sourceFilePath = path;
string input = path;
if (outputType == ScreenRecordOutput.GIF)
{
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, "gif"));
screenRecorder.FFmpegEncodeAsGIF(sourceFilePath, path, Program.ToolsFolder);
screenRecorder.FFmpegEncodeAsGIF(input, path, Program.ToolsFolder);
}
else if (taskSettings.CaptureSettings.ScreenRecordTwoPassEncoding)
{
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, taskSettings.CaptureSettings.FFmpegOptions.Extension));
screenRecorder.FFmpegEncodeVideo(input, path);
DebugHelper.WriteLine("Two pass encoding result:\nInput file size: {0}\nOutput file size: {1}",
new FileInfo(input).Length.ToSizeString(), new FileInfo(path).Length.ToSizeString());
}
if (taskSettings.CaptureSettings.RunScreencastCLI)
if (taskSettings.CaptureSettings.RunScreencastCLI && !taskSettings.CaptureSettings.ScreenRecordTwoPassEncoding)
{
VideoEncoder encoder = Program.Settings.VideoEncoders[taskSettings.CaptureSettings.VideoEncoderSelected];
path = Path.Combine(taskSettings.CaptureFolder, TaskHelpers.GetFilename(taskSettings, encoder.OutputExtension));
screenRecorder.EncodeUsingCommandLine(encoder, sourceFilePath, path);
screenRecorder.EncodeUsingCommandLine(encoder, input, path);
}
}
}
@ -283,7 +286,7 @@ private static void StartRecording(ScreenRecordOutput outputType, TaskSettings t
if (screenRecorder != null)
{
if ((outputType == ScreenRecordOutput.GIF || taskSettings.CaptureSettings.RunScreencastCLI) &&
if ((outputType == ScreenRecordOutput.GIF || taskSettings.CaptureSettings.RunScreencastCLI || taskSettings.CaptureSettings.ScreenRecordTwoPassEncoding) &&
!string.IsNullOrEmpty(screenRecorder.CachePath) && File.Exists(screenRecorder.CachePath))
{
File.Delete(screenRecorder.CachePath);