使用 Accord.net 录制视频 (AForge)

Record video with Accord.net (AForge)

我使用 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 时出现以下错误:

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

您可以尝试放置一些计时器以了解哪个操作正在减慢进程。(秒表 class 将是完美的)
我不知道你的框架的格式,但通常转换为位图是瓶颈,AForge 非常快。此外,您可以将时间戳传递给 WriteVideoFrame 方法,然后 Open 中指示的帧速率仅用于重播视频。 Aforge会在正确的时间以正确的顺序排列帧。
这是根据我的经验。希望对你有帮助。

"Slow motion" 可能有多种原因。首先,您需要知道生成 NewFrames 的实际帧率是否太慢(那么多少 HZ?)或者 CPU 是否太忙。或者是显卡。

对于与 DirectShow 相关的所有内容,我强烈建议使用 GraphEdit 和 AMCap 来查看设备的实际功能。 AForge 容易过采样,我不会怀疑它在 Accord 中是否类似。

此外,您始终可以依靠良好的旧 processexplorer 来查看负载(如果有)是由 SYSTEM 或 INTERRUPRS 引起的,还是实际上是由您的可执行文件产生的。

发生在我​​身上的另一件事是 PictureBox。 VideoSourcePlayer 好得多,最后我自己做了 blitting。

值得一提的是这三个优化:

  • 不要使用 Bitmap.GetPixel!
  • 使用videoSource.DisplayPinPropertyPage(IntPtr.Zero)
  • 检查视频流的颜色space

Bitmap.GetPixel

GetPixel 的问题在于,它真的很慢,因为它必须 unmanage/manage 对每个像素进行大量处理。这很好,因为您只需要少量调用并在加载的位图上使用它。如果您想每隔几毫秒在所有 pixelx 上使用它,那肯定是错误的方法。这种现象的 CPU 负载与您的应用程序相关联,并将在进程资源管理器中显示。 最后,我从这里开始编写了自己的像素例程:Work with bitmap faster with Csharp 如果你只是想要一个内核或比我需要的更普通的东西,实现一个 自定义 Filter.

VideoSource.DisplayPinPropertyPage

如果您使用 AForge 内置机制来选择分辨率和启动视频流,则无法设置帧率(我不认为这是 AForge 中的错误)。所以 AForge 总是选择最高的帧率。如果绕过 videoSource.VideoCapabilities 并直接显示本机设备配置对话框(正式名称为 "Video Capture Pin Properties Dialog")。您可以在那里手动设置所有参数,对话框将填写适当的帧率。这样,您将在回调中获得 "real"(硬件)刷新率。此过采样的 CPU 负载发生在 SYSTEM 进程中。

颜色space转换

在我的例子中,相机支持三种输出格式:YUY2、MJPG 和 RGB24。根据您使用的格式,图像由 AForge 转换为 RGB(我认为实际上是 ARGB32)。所有三种格式的渲染 DirectShow 图形都不同,并且 RGB 显然比其他格式使用的 CPU 少得多,因为颜色转换非常简单。此转换的负载也显示在 SYSTEM 进程中,而不是在您的应用程序 exe 中。

您可以使用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)
        {

        }
    }
}

试试这个

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)
        {


        }

    }
}

如果您在 newFrame 处理程序 Func 上写入文件,将导致 fastplaqy/slow 在某些编解码器 VP8-VP9 等上进行记录(mpeg4 无效),但如果您在某个时间启动不同的计时器来写入,您就可以解决问题