C# 如何让FFMPEG使用与输入相同的时间构建视频?
我试图通过从C#console应用程序将屏幕截图传输到FFMPEG,来创建用户的屏幕动作视频。我每秒发送10帧。最后一段视频的帧数与我发送的帧数完全相同(即:10秒视频有100帧)。但是,视频的时间不匹配。通过下面的代码,我从490751毫秒的输入中获得了价值7米47秒的视频。我发现PTS让我更接近,但感觉我做错了什么C# 如何让FFMPEG使用与输入相同的时间构建视频?,c#,video,ffmpeg,screenshot,C#,Video,Ffmpeg,Screenshot,我试图通过从C#console应用程序将屏幕截图传输到FFMPEG,来创建用户的屏幕动作视频。我每秒发送10帧。最后一段视频的帧数与我发送的帧数完全相同(即:10秒视频有100帧)。但是,视频的时间不匹配。通过下面的代码,我从490751毫秒的输入中获得了价值7米47秒的视频。我发现PTS让我更接近,但感觉我做错了什么 private const int VID_FRAME_FPS = 10; private const double PTS = 2.4444; ///
private const int VID_FRAME_FPS = 10;
private const double PTS = 2.4444;
/// <summary>
/// Generates the Videos by gathering frames and processing via FFMPEG.
/// Deletes the generated Frame images after successfully compiling the video.
/// </summary>
public static void RecordScreen(string pathToOutput)
{
Logger.log.Info("Launching FFMPEG ....");
String arg = "-f image2pipe -i pipe:.bmp -filter:v \"setpts = " + PTS + " * PTS\" -r " + VID_FRAME_FPS + " -pix_fmt yuv420p -qscale:v 5 -vcodec libvpx -bufsize 30000k -y \"" + pathToOutput + "\\VidOut.webm\"";
//String arg = "-f image2pipe -i pipe:.bmp -filter:v \"setpts = " + PTS + " * PTS\" -r " + VID_FRAME_FPS + " -pix_fmt yuv420p -qscale:v 5 -vcodec libx264 -bufsize 30000k -y \"" + pathToOutput + "\\VidOut.mp4\"";
Process launchingFFMPEG = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = arg,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true
}
};
launchingFFMPEG.Start();
System.Drawing.Image img;
Stopwatch stopWatch = Stopwatch.StartNew(); //creates and start the instance of Stopwatch
int sleep;
Stopwatch vidTime = Stopwatch.StartNew();
do
{
img = Capture.GetScreen();
img.Save(launchingFFMPEG.StandardInput.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp);
img.Dispose();
sleep = 10 * VID_FRAME_FPS - (int)stopWatch.ElapsedMilliseconds;
if (sleep > 0)
{
Logger.log.Info("Captured frame, sleeping " + sleep + " milliseconds.");
Thread.Sleep(sleep);
}
stopWatch.Restart();
} while (workerThread.IsAlive);
Logger.log.Debug("Video Time: " + vidTime.ElapsedMilliseconds);
launchingFFMPEG.StandardInput.Flush();
launchingFFMPEG.StandardInput.Close();
launchingFFMPEG.Close();
}
private const int VID_FRAME_FPS=10;
私人常数双点=2.4444;
///
///通过采集帧并通过FFMPEG进行处理来生成视频。
///成功编译视频后删除生成的帧图像。
///
公共静态无效记录屏幕(字符串路径输出)
{
Logger.log.Info(“启动FFMPEG…”);
String arg=“-f image2pipe-i pipe:.bmp-filter:v\”setpts=“+PTS+”*PTS\”-r“+VID_FRAME_FPS+”-pix_fmt yuv420p-qscale:v 5-vcodec libvpx-bufsize 30000k-y\”+路径输出+“\\VidOut.webm\”;
//String arg=“-f image2pipe-i pipe:.bmp-filter:v\”setpts=“+PTS+”*PTS\”-r“+VID_FRAME_FPS+”-pix_fmt yuv420p-qscale:v 5-vcodec libx264-bufsize 30000k-y\”+路径输出+“\\VidOut.mp4\”;
进程启动FFMPEG=新进程
{
StartInfo=新流程StartInfo
{
FileName=“ffmpeg”,
参数=arg,
UseShellExecute=false,
CreateNoWindow=true,
重定向标准输入=true
}
};
启动ffmpeg.Start();
系统图、图像img;
Stopwatch Stopwatch=Stopwatch.StartNew();//创建并启动Stopwatch实例
智力睡眠;
秒表vidTime=Stopwatch.StartNew();
做
{
img=Capture.GetScreen();
保存(启动ffmpeg.StandardInput.BaseStream,System.Drawing.Imaging.ImageFormat.Bmp);
img.Dispose();
睡眠=10*VID_FRAME_FPS-(int)秒表。延时百万秒;
如果(睡眠>0)
{
Logger.log.Info(“捕获帧,休眠”+休眠+“毫秒”);
睡眠;
}
stopWatch.Restart();
}while(workerThread.IsAlive);
Logger.log.Debug(“视频时间:+vidTime.elapsedmillyses”);
启动FFMPEG.StandardInput.Flush();
启动FFMPEG.StandardInput.Close();
启动ffmpeg.Close();
}
没有PTS有没有办法做到这一点?如果我需要PTS,正确的值是多少?2.5656的PTS似乎接近正确
所有相关文档都指向只使用-r(framerate命令),但这不起作用(因为我正在使用它)
注意:我只使用H.264对ffprobe进行调试,我计划在解决此问题后切换回webm。我试图避免H.256和MP4专利。我建议使用以下语法:
String arg = "-f image2pipe -framerate " + VID_FRAME_FPS + " -i pipe:.bmp -pix_fmt yuv420p -qscale:v 5 -vcodec libx264 -bufsize 30000k -y \"" + pathToOutput + "\\VidOut.mp4\"";
这假设发送帧之间的间隔是固定的,即每个帧在前一帧之后发送100ms。如果存在可变定时或帧速率,则需要不同的方法
顺便说一句,libx264将忽略
-qscale:v
,因此最好使用-crf N
,其中18到28是N的一个良好范围。它起作用了。接受一件事,我忘了在我的问题中提到,我使用的是webm而不是H.264。我已经切换到H.264进行测试,因为ffprobe无法从webm读取帧。事实上,任何非专利格式都很好。对webm有什么想法吗?同样的方法也适用于webm。谢谢@Mulvya,你是对的,事实证明,对于webm来说,我能在一秒钟内录制的帧数要慢得多。叹息