我目前正在做一个项目,我需要在C#中使用ffmpeg从视频中提取帧。然而,我面临着帧速率慢和资源使用率高的问题。我使用的代码如下:
private bool move = false;
private int master_frame = 0;
private void pic()
{
using (Process process = new Process())
{
process.StartInfo.FileName = "C:/Users/lenovo/Desktop/ffmpeg.exe";
process.StartInfo.Arguments = $"-i \"C:/Users/lenovo/Desktop/New folder/video.mp4\" -vf \"select=gte(n\\,{master_frame})\" -vframes 1 -q:v 2 -f image2pipe -c:v bmp -";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
using (MemoryStream outputStream = new MemoryStream())
{
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = process.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, bytesRead);
}
pictureBox1.Invoke((MethodInvoker)(() =>
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = new Bitmap(outputStream);
}));
}
}
}
private async void panel1_MouseUp(object sender, MouseEventArgs e)
{
move = true;
await Task.Run(() =>
{
while (move)
{
pic();
master_frame++;
}
});
}
字符串
问题是帧速率相当慢,并且资源使用率高于预期。我怀疑阅读ffmpeg的输出流并从MemoryStream为每个帧创建位图可能会导致性能问题。
我将感谢任何关于如何优化帧提取过程以获得更好的性能和更低的资源使用的见解或建议。有没有更有效的方法在C#中使用ffmpeg从视频中提取帧?是否有其他方法或优化可以帮助提高帧提取速度?
提前感谢您的帮助和建议!
1条答案
按热度按时间shstlldc1#
**TLDR:**非常低的帧率是由于启动
ffmpeg
进程和ffmpeg
开始发送实际数据之间存在巨大的延迟。My answer to your other post ("How to Play a Video in a PictureBox Using FFmpeg in C#?"), which is essentially the same as this post, shows how to do it much faster, although still not at 60 frames per second
性能测量
使用
Stopwatch
es,我测量了代码中每一步所花费的时间。代码
下面是我的measuments代码:
字符串
测量
这是我的结果,对于一个480 p 30 fps的视频,长度为1分36秒,在我的电脑上,我平均有:
110ms
,峰值在300ms
附近<1ms
个1ms
个<1ms
个2ms
提醒一下,要达到60 FPS,您需要在
16.67ms
或33.33ms
中渲染每一帧以获得30 FPS。从中我们能理解什么
这里的罪魁祸首是读取ffmpeg发送的流所花费的时间,这是相当明显的。
顺便说一下,我也试过@Charlieface的建议,不,使用
process.StandardOutput.BaseStream.CopyTo(outputStream);
并没有保存多少时间。事实上,你可以直接使用
new Bitmap(process.StandardOutput.BaseStream);
,但这并不能使整个事情变得更快。真实的的问题
起初,指责ffmpeg将我们要求的帧转换为所需格式的速度太慢似乎是显而易见的。
正如@ChristophRackwitz所指出的,这确实是代码缓慢的一个因素。
当请求ffmpeg获取特定帧时,它实际上需要解码直到该帧的整个视频流,从而导致随着时间的推移,每帧所花费的时间越来越多。
这不是一个问题,虽然在我身边,因为我用了一个非常低的质量和帧率的视频。因此,我的测量是极简主义的,在实践中,渲染每一帧所需的时间可能比我测量的要长得多。我让你想象一下,获得一个1小时长的4K 144 fps视频的最后一帧所需的时间。
还有另一个因素,它是关于流程创建的。
事情是这样的,当你启动一个新的ffmpeg进程示例时,会发生以下情况:
这是相当多的,对不对?
这里的问题是,在所有这些步骤中,只有一个步骤对每个帧都是唯一的,那就是“ffmpeg终于可以完成它的工作并将其发送到流”。
所有其他步骤,这需要大量的时间,重复每帧。
因此,my answer to your other post,其中不是为每个帧打开一个流,而是为所有帧打开一个流并真实的渲染它们。这避免了为每个帧创建新的处理示例,并且还避免了必须从每个帧的开始解码整个视频流。
最终解决方案
你不能用你的代码目前的工作方式来提高它的速度,你需要从每帧一个进程切换到每帧一个进程,这就是my answer to your other post所做的。
感谢阅读。