gpt4 book ai didi

c# - 如何计时从视频文件中显示和提取帧?

转载 作者:行者123 更新时间:2023-12-05 01:49:23 25 4
gpt4 key购买 nike

目标是控制 BackGroundWorker 的 DoWork 事件中的帧提取速度。
我尝试了 Thread.Sleep(),但它引发了异常。

这就是我想要做的。在上面和底部对其进行了描述。

using Accord.Video;
using Accord.Video.FFMPEG;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

using (var vFReader = new VideoFileReader())
{
vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
trackBar1.Maximum = (int)vFReader.FrameCount;
}
}

private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (var i = 0; i < vFReader.FrameCount; i++)
{
backgroundWorker1.ReportProgress(0, vFReader.ReadVideoFrame());
}

// Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
vFReader.Close();
}
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = (Image)e.UserState;
}

private void Form1_Resize(object sender, EventArgs e)
{
label1.Text = this.Size.ToString();
}
}

它工作正常但速度太快。我想使用计时器,或允许我控制帧提取速度的东西。

最佳答案

我建议对当前代码进行一些更改(实际上相当多:)。

要点:

  1. 创建一个异步方法来执行视频播放。 VideoFileReader在 ThreadPool 线程(实际上是 2 个)上工作,它不会导致表单卡住
  2. 使用IProgress<T>委托(delegate)(属于 Progress<Bitmap> 类型,在此处命名为 videoProgress)将新数据编码到 UI 线程,用于更新 PictureBox 控件。委托(delegate)方法名为 Updater
  3. 使用单个位图对象,设置为 Image图片框的属性
  4. 使用派生自该 Bitmap 的 Graphics 对象来绘制视频帧。这允许包含使用的资源。 PictureBox 只是无效化,以显示 Bitmap 的当前内容
  5. 允许视频播放方法接受帧速率值,此处设置为每秒 25 帧。当然,可以适配放慢或加快播放(注意,设置超过每秒32~35帧,你开始丢失一些帧)
  6. 使用 CancellationTokenSource向视频播放方法发出信号以停止播放并终止,当播放处于事件状态时按下停止按钮或关闭表单时

重要提示:

  • VideoFileReader 的位图返回必须处理。如果不这样做,您将看到图形资源消耗的增加,而且这种情况不会停止
  • 使用单个 Bitmap 并使用派生的 Graphics 对象绘制每个新帧,可以保留图形资源。如果在播放视频时让“诊断工具” Pane 保持打开状态,您会注意到没有泄漏任何资源并且内存使用量保持不变。
    当您打开此窗体并创建容器位图时,当然会略有增加,但是当关闭窗体时,会回收少量资源
  • 这还允许更平滑的过渡和更快的渲染速度(在播放视频时四处移动表单)。另外,尝试锚定/停靠 PictureBox,设置 SizeMode = Zoom并最大化表单(设置 PictureBox 的 Zoom 模式会影响性能,您应该改为调整位图的大小)

buttonStart_Click , buttonStop_ClickbuttonPause_Click是用于开始、停止和暂停播放的按钮的点击处理程序。
syncRoot object 在这里不是严格要求的,但保留在那里,它可能在某些时候有用

using System.Drawing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Accord.Video.FFMPEG;

public partial class Form1 : Form
{
Bitmap frame = null;
Graphics frameGraphics = null;
bool isVideoRunning = false;
IProgress<Bitmap> videoProgress = null;
private CancellationTokenSource cts = null;
private readonly object syncRoot = new object();
private static long pause = 0;

public Form1() => InitializeComponent();

private async void buttonStart_Click(object sender, EventArgs e) {
string fileName = "[The Video File Path]";

if (isVideoRunning) return;
isVideoRunning = true;

using (var videoReader = new VideoFileReader()) {
videoReader.Open(fileName);
frame = new Bitmap(videoReader.Width + 2, videoReader.Height + 2);
trackBar1.Maximum = (int)videoReader.FrameCount;
}

videoProgress = new Progress<Bitmap>(Updater);
cts = new CancellationTokenSource();
pictureBox1.Image = frame;
try {
frameGraphics = Graphics.FromImage(frame);
// Set the frame rate to 25 frames per second
int frameRate = 1000 / 25;
await GetVideoFramesAsync(videoProgress, fileName, frameRate, cts.Token);
}
finally {
StopPlayback(false);
frameGraphics?.Dispose();
pictureBox1.Image?.Dispose();
pictureBox1.Image = null;
buttonPause.Text = "Pause";
pause = 0;
isVideoRunning = false;
}
}

private void buttonStop_Click(object sender, EventArgs e) => StopPlayback(true);

private void buttonPause_Click(object sender, EventArgs e)
{
if (pause == 0) {
buttonPause.Text = "Resume";
Interlocked.Increment(ref pause);
}
else {
Interlocked.Decrement(ref pause);
buttonPause.Text = "Pause";
}
}

private void StopPlayback(bool cancel) {
lock (syncRoot) {
if (cancel) cts?.Cancel();
cts?.Dispose();
cts = null;
}
}

private void Updater(Bitmap videoFrame) {
using (videoFrame) frameGraphics.DrawImage(videoFrame, Point.Empty);
pictureBox1.Invalidate();
}

private async Task GetVideoFramesAsync(IProgress<Bitmap> updater, string fileName, int intervalMs, CancellationToken token = default) {
using (var videoReader = new VideoFileReader()) {
if (token.IsCancellationRequested) return;
videoReader.Open(fileName);

while (true) {
if (token.IsCancellationRequested) break;
// Resumes on a ThreadPool Thread
await Task.Delay(intervalMs).ConfigureAwait(false);

if (Interlocked.Read(ref pause) == 0) {
var frame = videoReader.ReadVideoFrame();
if (frame is null) break;
updater.Report(frame);
}
}
}
}

protected override void OnFormClosing(FormClosingEventArgs e) {
if (isVideoRunning) StopPlayback(true);
base.OnFormClosing(e);
}
}

关于c# - 如何计时从视频文件中显示和提取帧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74157315/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com