我使用Accord.net (AForge)连接到网络摄像头并录制视频但是存储的视频是慢动作的……这是我的项目:
using AForge.Video;
using AForge.Video.DirectShow;
using AForge.Video.FFMPEG;
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace CaptureWebcam
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private string path = Path.GetDirectoryName(Application.ExecutablePath) + @"Videos";
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
private Bitmap video;
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
video = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void btnStartCam_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
videoSource = captureDevice.VideoDevice;
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
timer1.Enabled = true;
}
//videoSource.DisplayPropertyPage(IntPtr.Zero);
}
private Thread workerThread = null;
private bool stopProcess = false;
private void recordLiveCam()
{
if (!stopProcess)
{
while (isRecord)
{
FileWriter.WriteVideoFrame(video);
}
FileWriter.Close();
}
else
{
workerThread.Abort();
}
}
private void btnRecord_Click(object sender, EventArgs e)
{
//try
//{
isRecord = true;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open(path + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
stopProcess = false;
workerThread = new Thread(new ThreadStart(recordLiveCam));
workerThread.Start();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message);
//}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnStopRecord_Click(object sender, EventArgs e)
{
stopProcess = true;
isRecord = false;
FileWriter.Close();
workerThread.Abort();
video = null;
}
private void btnStopCam_Click(object sender, EventArgs e)
{
try
{
if (videoSource.IsRunning)
{
videoSource.SignalToStop();
videoSource.Stop();
pictureBox1.Image = null;
pictureBox1.Invalidate();
if (FileWriter.IsOpen)
{
FileWriter.Close();
video = null;
}
}
else
return;
}
catch
{
videoSource.Stop();
FileWriter.Close();
video = null;
}
}
float fts = 0.0f;
private void timer1_Tick(object sender, EventArgs e)
{
fts = videoSource.FramesReceived;
label1.Text = "Frame Rate : " + fts.ToString("F2") + " fps";
}
}
}
和当点击btnStopRecord以下错误:
附加信息:试图读写受保护的内存。这通常表明其他内存已损坏。
"慢动作"可能有多种原因。首先,您需要知道NewFrames get产生的实际帧率是否很慢(那么是多少HZ ?),或者CPU是否太忙了。或者显卡。
对于所有与DirectShow相关的内容,我强烈建议使用graphhedit和AMCap来查看设备的真正功能。AForge倾向于过度采样,我不会怀疑Accord是否也有类似的情况。
同样,你可以总是依靠好的老procesexplorer来查看加载(如果有的话)是由SYSTEM或INTERRUPRS引起的,还是由你的可执行文件产生的。
发生在我身上的另一件事是PictureBox。VideoSourcePlayer要好得多,我只好自己来做数据传输了。
值得一提的是这三个优化:
- 不使用
Bitmap.GetPixel
! - 使用
videoSource.DisplayPinPropertyPage(IntPtr.Zero)
- 检查视频流的色彩空间
位图。获取像素
GetPixel的问题是,它真的很慢,因为它必须为每个像素unmanage/manage很多。只要您只获得少量调用并在加载的位图上使用它就可以了。如果你想每隔几毫秒在所有像素上使用它,这绝对是错误的方法。这种现象导致的CPU负载与您的应用程序相关联,并将在进程资源管理器中显示。最后,我从这里开始编写了自己的像素例程:使用Csharp更快地处理位图如果你只是想要一个内核或者比我需要的更普通的东西,实现a自定义过滤器。
VideoSource。DisplayPinPropertyPage
如果你使用AForge内置的机制来选择分辨率和初始化视频流,你不能设置帧率(我不认为这是AForge的一个bug)。所以AForge总是选择最高的帧率。如果你绕过videoSource。并直接显示本机设备配置对话框(它的正式名称为"视频捕获引脚属性对话框")。在那里,你可以手动设置所有参数,对话框将填充适当的帧率。这样,您将在回调中获得"真实的"(硬件)刷新率。此过采样的CPU负载发生在SYSTEM进程中。
色彩空间转换
在我的情况下,相机支持三种输出格式:YUY2, MJPG和RGB24。根据您使用的格式,图像由AForge转换为RGB(我认为实际上是ARGB32)。这三种格式呈现的DirectShow图形是不同的,RGB显然比其他格式使用更少的CPU,因为颜色转换非常简单。此转换产生的负载也显示在SYSTEM进程中,而不是显示在应用程序的exe中。
您可以尝试放置一些计时器来了解哪个操作减慢了进程。(秒表类将是完美的)
我不知道你的帧格式,但通常转换到位图是瓶颈,AForge相当快。此外,您可以将时间戳传递给WriteVideoFrame方法,然后在Open中指示的帧率仅用于重播视频。Aforge将在正确的时间以正确的顺序协调帧。
这是我的经验之谈。
试试这个
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AForge;
using AForge.Video.DirectShow;
using Accord.Video.FFMPEG;
using System.IO;
using AForge.Video;
using System.Threading;
namespace New_Project_2
{
public partial class Form1 : Form
{
private VideoCaptureDeviceForm captureDevice;
private FilterInfoCollection videoDevice;
private VideoCaptureDevice videoSource;
private VideoFileWriter FileWriter = new VideoFileWriter();
bool isRecord = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (isRecord == true)
{
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
FileWriter.WriteVideoFrame((Bitmap)eventArgs.Frame.Clone());
}
}
catch (Exception)
{
throw;
}
}
private void button1_Click(object sender, EventArgs e)
{
videoDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);
captureDevice = new VideoCaptureDeviceForm();
if (captureDevice.ShowDialog(this) == DialogResult.OK)
{
isRecord = true;
int h = captureDevice.VideoDevice.VideoResolution.FrameSize.Height;
int w = captureDevice.VideoDevice.VideoResolution.FrameSize.Width;
FileWriter.Open("d:\" + "recorded at " + DateTime.Now.ToString("HH-mm-ss") + ".mp4", w, h, 25, VideoCodec.MPEG4);
videoSource = captureDevice.VideoDevice;
videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
videoSource.Start();
}
//videoSource.DisplayPropertyPage(IntPtr.Zero)
}
private void button2_Click(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
isRecord = false;
FileWriter.Close();
}
private void button4_Click(object sender, EventArgs e)
{
}
}
}
可以使用emguCV录制视频
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu;
using Emgu.CV;
using Emgu.CV.Structure;
namespace Load_Images
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
double TotalFrame;
double Fps;
int captureFrameNo;
VideoCapture capture;
VideoWriter writer;
bool isCapturing = false;
private void button1_Click(object sender, EventArgs e)
{
if (isCapturing == false)
{
capture = new VideoCapture(0);
TotalFrame = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameCount);
Fps = capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps);
isCapturing = true;
int fourcc = fourcc = VideoWriter.Fourcc('H', '2', '6', '4');
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 2048);
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 1024);
//int fourcc = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FourCC));
int width = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth));
int height = Convert.ToInt32(capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight));
string destin_path = "D:\out.mp4";
writer = new VideoWriter(destin_path, fourcc, Fps, new Size(width, height), true);
capture.ImageGrabbed += Capture_ImageGrabbed;
capture.Start();
}
}
private void Capture_ImageGrabbed(object sender, EventArgs e)
{
try
{
if (isCapturing == true)
{
if (capture == null)
{
return;
}
Mat m = new Mat();
capture.Retrieve(m);
writer.Write(m);
pictureBox1.Image = m.ToImage<Bgr, byte>().Bitmap;
}
}
catch (Exception)
{
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (isCapturing == true) {
capture.Stop();
isCapturing = false;
}
}
private void button3_Click(object sender, EventArgs e)
{
if (capture != null) {
capture.Pause();
}
}
private void button4_Click(object sender, EventArgs e)
{
if (writer.IsOpened)
{
isCapturing = false;
writer.Dispose();
}
MessageBox.Show("Completed");
}
private void Form2_Load(object sender, EventArgs e)
{
}
}
}
如果你在newFrame处理程序Func上写文件会导致一些编解码器VP8-VP9等(mpeg4不生效)上的快速播放/慢速记录,但如果你在某些时间开始不同的计时器来写你解决你的问题